相互引用的Python类

17 投票
9 回答
26234 浏览
提问于 2025-04-15 12:29

我有两个类,它们互相引用,但编译器显然不高兴。这有什么办法可以解决吗?

补充说明

其实我的代码和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 个回答

2

只要你在一个方法里,就可以访问这个类的对象。

所以上面的例子如果把 creator.register(Y) 移到 __init__ 里面是没有问题的。不过,你不能在方法外面有类之间的循环引用。

5

这是个很好的问题。虽然其他人已经回答过了,我还是想再提供一个例子。

先来看这个程序。

@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__ 方法在需要的时候就能正常工作。

28

在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,再创建依赖于XY的属性,如果这样更简单的话。

希望这对你有帮助 :)

撰写回答