阅读 chepner 的回答中的 cmets,您似乎想要一种能够进行绑定的惰性方法。请注意,在另一个答案中允许延迟分配“next_item”仍然是“正确的做法”。
这很容易做到,但是它们,您仍然依赖硬编码的其他实例名称。例如,一些 ORM 框架,因为它们允许定义类之间的相互关系,所以允许您将其他类作为字符串而不是实际的类对象插入。
但是字符串对于在模块顶层定义的对象会很好用,因为它们可以作为全局变量获取 - 但如果您在函数或方法中使用循环类,则不会工作。将返回具有实例名称的非局部变量的可调用对象可以工作:
from types import FunctionType
class CyclicClass:
def __init__(self, name, next_item=None):
self.name = name
self.next_item = next_item
def print_next(self):
print(self.next_item)
@property
def next_item(self):
if isinstance(self._next_item, FunctionType):
self._next_item = self._next_item()
return self._next_item
@next_item.setter
def next_item(self, value):
self._next_item = value
并在交互式解释器上进行测试:
In [23]: def test():
...: inst1 = CyclicClass("inst1", lambda: inst2)
...: inst2 = CyclicClass("inst2", inst1)
...: return inst1, inst2
...:
In [24]: i1, i2 = test()
In [25]: i1.next_item.name
Out[25]: 'inst2'
但是这种方法相当幼稚-如果您将您的实例放入列表或其他数据结构中,则不会起作用,除非您有一个很好的时机将属性渲染触发为真正的参考-此时它是最好还是允许延迟分配给 next_item。
并不是说如果“名称”属性是唯一的,您可以修改上面的代码以拥有所有实例的全局注册表,并传递一个字符串来标识您的实例。这可能符合您的需求 - 但仍然会比允许后期设置 next_item 增加更多复杂性
cyclic_names = {}
class CyclicClass:
...
@property
def next_item(self):
if isinstance(self._next_item, str):
self._next_item = cyclic_names[self._next_item]
return self._next_item
@next_item.setter
def next_item(self, value):
self._next_item = value
@property
def name(self):
return self._name
@name.setter(self)
def name(self, value):
if hasattr(self, "_name"):
del cyclic_names[value]
if value in cyclic_names:
raise ValueError("An object with this name already exists")
cyclic_names[value] = self
def __del__(self):
del cyclic_names[self._name]
如您所见,正确完成这项工作的复杂性迅速增加,这可能是您项目中缺陷的来源 - 但如果考虑到所有细微差别,仍然可以完成。 (例如,我会在全局对象索引上使用弱引用)