假设class A
的成员类型为class B
,而class B
的成员类型为class A
。
在Scala或Kotlin中,在这种情况下,您可以以任何顺序定义类而不必担心,因为即使在case / data类中,第一个定义的类也可以照常使用第二个定义的类。
但是在Python中,以下代码
class A:
b = B()
class B:
a = A()
因为定义class B
时未定义class A
,所以引发了编译错误。
您可以解决这种简单的情况,例如this answer
class A:
pass
class B:
a = A()
A.b = B()
但是,这种方法不适用于Python中的数据类,因为在定义数据类后分配成员将不会更新数据类的自动生成的方法,这使“数据类”的使用变得无用。
@dataclass
class A:
b: B # or `b: Optional[B]`
@dataclass
class B:
a: A # or `a: Optional[A]`
如何避免这个问题?
答案 0 :(得分:8)
有几种解决循环依赖项的方法,请参见Type hints: solve circular dependency
您始终可以手动应用装饰器(并更新注释),例如@Nearoo的答案显示。
但是,“转发声明”该类可能会更容易:
class A:
pass
@dataclass
class B:
a: A
@dataclass
class A:
b: B
或仅使用前向引用:
@dataclass
class B:
a: 'A'
@dataclass
class A:
b: B
最干净的方法是导入Python 4.0's behavior(如果可以的话):
from __future__ import annotations
@dataclass
class B:
a: A
@dataclass
class A:
b: B
答案 1 :(得分:4)
只有在我们将字段dataclass
注入到b
中之后,才可以应用A
装饰器来实现您的目标。为此,我们只需要在A
的{{1}}字段中添加类型注释
以下代码解决了您的问题:
__annotations__
关于此方法的安全性和有效性,PEP 524指出
在模块或类级别的..,如果要注释的项目是简单名称,则它和注释将存储在该模块或类的__annotations__属性中。 [此属性]是可写的,因此允许:
class A: b: None # Note: __annotations__ only exists if >=1 annotation exists @dataclass class B: a: A A.__annotations__.update(b=B) # Note: not the same as A.b: B A = dataclass(A) # apply decorator
因此以后通过编辑__annotations__['s'] = str
添加类型注释与在类定义中定义它相同。
答案 2 :(得分:2)
由于python是脚本语言-无法用@dataclass
来实现。因为python中没有“自动装配”(依赖注入)机制。
此时,如果您需要循环依赖-您应该正常使用类之一。
class A:
b = None
@dataclass
class B:
a: A
a = A()
a.b = B(a)
Python编译器遍历每一行,而无需从类/函数定义中跳转。并且当编译器/解释器看到下面的行b: B
并且之前没有看到B
类时-它将抛出异常NameError: name 'B' is not defined
我想相信有办法做到这一点(@dataclass
的循环依赖),但事实是残酷的。 (您可以用Java /其他语言做很多事情,而不能用python做很多事情。这句话的另一个方向也很真实。)