如何解决从不同文件中为 类 指定类型提示时的递归问题
how to solve the recursion problem when specifying type hints for classes from different files
如何解决从不同文件类指定类型提示时的递归问题
models1.py
from models2 import Second
@dataclass
class First:
attribute: Second
models2.py
from models1 import First
@dataclass
class Second:
attribute: First
在实际代码中,我想将 SentInvoice
和 User
模型拆分到不同的文件中。
class User(models.Model):
user_id = fields.BigIntField(index=True, unique=True)
username = fields.CharField(32, unique=True, index=True, null=True)
first_name = fields.CharField(255, null=True)
last_name = fields.CharField(255, null=True)
language = fields.CharField(32, default="ru")
balance: Balance = fields.OneToOneField("models.Balance", on_delete=fields.CASCADE)
sent_invoice: fields.OneToOneNullableRelation["SentInvoice"] # here
registered_user: RegisteredUser
@classmethod
async def create(cls: Type[MODEL], **kwargs: Any):
return await super().create(**kwargs, balance=await Balance.create())
class SentInvoice(models.Model):
amount = fields.DecimalField(17, 7)
shop_id = fields.CharField(50)
order_id = fields.CharField(10, null=True)
email = fields.CharField(20, null=True)
currency = fields.CharField(5, default="RUB", description="USD, RUB, EUR, GBP")
user: fields.ForeignKeyRelation[User] = fields.ForeignKeyField("models.User", on_delete=fields.CASCADE) # here
created_invoice: fields.OneToOneNullableRelation[CreatedInvoice]
async def send(self) -> CreatedInvoice:
cryptocloud = config.payment.cryptocloud
async with aiohttp.ClientSession(headers={"Authorization": f"Token {cryptocloud.api_key}"}) as session:
async with session.post(cryptocloud.create_url, data=dict(self)) as res:
created_invoice = await CreatedInvoice.create(**await res.json(), sent_invoice=self)
return created_invoice
您需要使用两种特定于 python 中的类型提示的技术,1) forward references, and 2) importing types within a TYPE_CHECKING
guard (check e.g. 以获得对其含义的更详细解释)。第一个允许您在运行时引用解释器不知道的类型,而后者在“类型检查上下文”中解析类型。
长话短说:
models1.py
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from models2 import Second
@dataclass
class First:
attribute: "Second"
models2.py
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from models1 import First
@dataclass
class Second:
attribute: "First"
使用 python3.8
或更高版本执行文件应该没有任何问题[1],并且在 python3.7
中也可以工作 with a __futures__
import. 运行 mypy
文件也应该可以正常工作:
$ mypy models1.py models2.py
Success: no issues found in 2 source files
[1] 正如评论所指出的,创建您的 First
/Second
类 的实际实例也会通过类型检查是不可能的,但我假设这是一个玩具示例,并且您的真实代码具有例如 Optional
.
属性之一
如何解决从不同文件类指定类型提示时的递归问题
models1.py
from models2 import Second
@dataclass
class First:
attribute: Second
models2.py
from models1 import First
@dataclass
class Second:
attribute: First
在实际代码中,我想将 SentInvoice
和 User
模型拆分到不同的文件中。
class User(models.Model):
user_id = fields.BigIntField(index=True, unique=True)
username = fields.CharField(32, unique=True, index=True, null=True)
first_name = fields.CharField(255, null=True)
last_name = fields.CharField(255, null=True)
language = fields.CharField(32, default="ru")
balance: Balance = fields.OneToOneField("models.Balance", on_delete=fields.CASCADE)
sent_invoice: fields.OneToOneNullableRelation["SentInvoice"] # here
registered_user: RegisteredUser
@classmethod
async def create(cls: Type[MODEL], **kwargs: Any):
return await super().create(**kwargs, balance=await Balance.create())
class SentInvoice(models.Model):
amount = fields.DecimalField(17, 7)
shop_id = fields.CharField(50)
order_id = fields.CharField(10, null=True)
email = fields.CharField(20, null=True)
currency = fields.CharField(5, default="RUB", description="USD, RUB, EUR, GBP")
user: fields.ForeignKeyRelation[User] = fields.ForeignKeyField("models.User", on_delete=fields.CASCADE) # here
created_invoice: fields.OneToOneNullableRelation[CreatedInvoice]
async def send(self) -> CreatedInvoice:
cryptocloud = config.payment.cryptocloud
async with aiohttp.ClientSession(headers={"Authorization": f"Token {cryptocloud.api_key}"}) as session:
async with session.post(cryptocloud.create_url, data=dict(self)) as res:
created_invoice = await CreatedInvoice.create(**await res.json(), sent_invoice=self)
return created_invoice
您需要使用两种特定于 python 中的类型提示的技术,1) forward references, and 2) importing types within a TYPE_CHECKING
guard (check e.g.
长话短说:
models1.py
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from models2 import Second
@dataclass
class First:
attribute: "Second"
models2.py
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from models1 import First
@dataclass
class Second:
attribute: "First"
使用 python3.8
或更高版本执行文件应该没有任何问题[1],并且在 python3.7
中也可以工作 with a __futures__
import. 运行 mypy
文件也应该可以正常工作:
$ mypy models1.py models2.py
Success: no issues found in 2 source files
[1] 正如评论所指出的,创建您的 First
/Second
类 的实际实例也会通过类型检查是不可能的,但我假设这是一个玩具示例,并且您的真实代码具有例如 Optional
.