我有两个相互引用的类,但显然编译器抱怨。有没有办法解决这个问题?
修改
实际上我的代码与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)
希望这有助于澄清。任何建议。
答案 0 :(得分:15)
在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)
也就是说,创建X
的类属性,在Y
创建后引用Y
。反之亦然:首先创建Y
,然后创建X
,然后创建依赖Y
的{{1}}属性,如果这更容易的话。
希望这会有所帮助:)
答案 1 :(得分:2)
更新:我在回答后改变了问题。鉴于新问题,目前接受的解决方案更好。
你在说什么问题?
class A(object):
def __init__(self):
super(A, self).__init__()
def b(self):
return B()
class B(object):
def __init__(self):
super(B, self).__init__()
def a(self):
return A()
这编译并运行得很好。
答案 2 :(得分:2)
只要您在方法中工作,就可以访问类对象。
因此,如果在creator.register(Y)
内移动__init__
,则上述示例没有问题。但是,您不能对方法之外的类进行循环引用。
答案 3 :(得分:2)
错误是在类X的(可执行)定义期间尝试执行creator.register(Y)
,并且在该阶段,未定义类Y.理解这一点:class
和def
是执行的语句(通常在导入时);它们不是“声明”。
建议:告诉我们你想要达到的目标 - 也许是一个新问题。
答案 4 :(得分:0)
众所周知,这看起来像是不言而喻的矛盾,我们试图从它们在物理世界中诞生的角度来想象两个独立但相互依赖的实体。但是,在软件领域,我们经常会遇到所谓的“循环或相互引用”问题。在面向对象的设计中,这可能会变得更为严重,在这种设计中,互操作的软件元素通常是通过模仿物理元素的方式来定义并相互关联的,但是仍然是纯逻辑存在的。
在许多编程语言中,这些问题已通过在函数或类的签名形式(无主体定义)之前及时声明要引用的元素而得到解决。但是,对于诸如Python这样的基于脚本的语言,这种回避技巧似乎不再可用或有用。
在Python中,从软件工程的角度来看,我们最好采用“循环引用”,如下所示:
最好重新设计非循环的类;有几种方法(例如,类分解或组合,回调函数,观察者或订户模式等)使元素之间的引用发生在同一类中,被删除或反向。
如果元素之间的某些环形链线性化可能在质量或生产率等方面引起更严重的问题,我们可以采取另一种措施将其传统构造阶段分为两个阶段:创建和构造。 例如,两个朋友中注定要在观察对方的出生之后就绝对要出生的人,可以这样做:他们先出生,然后在任何有意义和可观察的场合发生之前就拥有了友谊。 请注意,如果我们在处理相互聚合的对象时遇到一些极端的复杂性或需要高度的完整性,则应用Factory模式将会有所收获。
答案 5 :(得分:0)
这是一个很好的问题。虽然其他人已经回答过了,我还是再举个例子吧。
考虑这个计划。
@dataclass
class A:
b: B
class B:
def __init__(self):
pass
b
现在是 class 级别的变量,该程序不起作用。在 Python 解释器加载(执行)类 B
的代码时,名称 A
未定义。与编译型语言(如C/C++)不同,解释器从头到尾逐个命令地执行代码,一次传递。由于 Python 在定义类 B
时需要知道 A
是什么,所以它失败了。 B
仅在稍后定义。
现在,考虑一个稍微不同的程序。
class A:
def __init__(self):
self.b = B()
class B:
def __init__(self):
pass
b
现在是一个对象级变量,这个程序可以工作。 Python 仍然在单程中从文件的开头到结尾执行代码,但是,现在它在读取 B
行时不需要知道 self.b = B()
是什么。这是因为 __init__
方法仅在有人想要构造类 A
的对象时执行。由于对象的构造将发生在程序稍后的某个地方(当 B
已经定义时),__init__
将在需要时正常工作。
答案 6 :(得分:-2)
问题很可能不是Python。我认为这是一个SQL问题。这些类通过转换为SQL查询的抽象层来创建表。 您试图从一个表中引用另一个当时尚不存在的表。
在SQL中,您可以通过先创建没有引用的表来解决此问题,然后修改它们以进行这些引用,
但是我不确定我的答案,所以请大量调查,如果Django的数据库抽象层不能很好地处理交叉引用,我会感到非常惊讶。