相互引用的Python类
我有两个类,它们互相引用,但编译器显然不高兴。这有什么办法可以解决吗?
补充说明
其实我的代码和Hank Gay用的稍微有点不同。所以Python确实可以处理某些类型的循环引用,但在以下情况下会报错。下面是我的代码,我得到了一个'名称Y未定义'的错误。
class X(models.Model):
creator = Registry()
creator.register(Y)
class Y(models.Model):
a = models.ForeignKey(X)
b = models.CharField(max_length=200)
希望这能帮助你理解。有任何建议吗?
9 个回答
只要你在一个方法里,就可以访问这个类的对象。
所以上面的例子如果把 creator.register(Y)
移到 __init__
里面是没有问题的。不过,你不能在方法外面有类之间的循环引用。
这是个很好的问题。虽然其他人已经回答过了,我还是想再提供一个例子。
先来看这个程序。
@dataclass
class A:
b: B
class B:
def __init__(self):
pass
b
现在是一个类级别的变量,但这个程序不工作。因为在 Python 解释器加载(执行)类 A
的代码时,B
这个名字还没有被定义。和编译语言(比如 C/C++)不同,解释器是从文件的开头到结尾一行一行地执行代码。由于 Python 在定义类 A
时需要知道 B
是什么,所以它失败了。B
只有在后面才被定义。
现在,我们来看一个稍微不同的程序。
class A:
def __init__(self):
self.b = B()
class B:
def __init__(self):
pass
b
现在是一个对象级别的变量,这个程序可以正常工作。Python 仍然是从头到尾一行一行地执行代码,但这次在读取 self.b = B()
这一行时,它不需要知道 B
是什么。这是因为 __init__
方法只有在有人想要创建类 A
的对象时才会被执行。由于对象的创建会在程序的后面某个地方发生(那时 B
已经被定义了),所以 __init__
方法在需要的时候就能正常工作。
在Python中,当一个类被加载时,类里面的代码就会被执行。
这到底是什么意思呢?;-)
来看下面这段代码:
class x:
print "hello"
def __init__(self): print "hello again"
当你加载包含这段代码的模块时,Python会打印出hello
。每当你创建一个x
的时候,Python会打印出hello again
。
你可以把def __init__(self): ...
看作是__init__ = lambda self: ...
,不过在这里没有Python lambda的限制。也就是说,def
其实是一个赋值,这可能解释了为什么在方法外的代码会被执行,而在方法内的代码不会。
当你的代码写成这样:
class X(models.Model):
creator = Registry()
creator.register(Y)
你在模块加载时引用了Y
,但此时Y
还没有值。你可以把class X
看作是一个赋值(不过我记不清创建匿名类的语法了;也许是调用type
?)
你可能想要做的是这样:
class X(models.Model):
pass
class Y(models.Model):
foo = something_that_uses_(X)
X.bar = something_which_uses(Y)
也就是说,在Y
被创建之后,创建X
的类属性来引用Y
。或者反过来:先创建Y
,然后是X
,再创建依赖于X
的Y
的属性,如果这样更简单的话。
希望这对你有帮助 :)