如何解决从不同文件中为 类 指定类型提示时的递归问题

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

在实际代码中,我想将 SentInvoiceUser 模型拆分到不同的文件中。

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.

属性之一