Create Circular References Between Class Instances?
Solution 1:
You need to create the objects first, then link them.
item_a = CyclicClass("Item A", None)
item_b = CyclicClass("Item B", item_a)
item_a.next_item = item_b
Think of the second argument to CyclicClass
as a convenience, rather than the primary way of linking two objects. You can emphasize that by making None
the default parameter value.
classCyclicClass:
def__init__(self, name, next_item=None):
self.name = name
self.next_item = next_item
defprint_next(self):
print(self.next_item)
item_a = CyclicClass("Item A")
# item_b = CyclicClass("Item B")# item_b.next_item = item_a
item_b = CyclicClass("Item B", item_a)
item_a.next_item = item_b
Solution 2:
Reading the comments on chepner's answer, it looks like you want a lazy approach to be able to do the binding. Please note that allowing late assignment of "next_item" as in the other answer is still the "right thing to do".
That can easily be done, but them, you'd still depend on the hardcoded other-instance name. Some ORM frameworks for example, since they allow one to define inter-relationships between classes, allow you to insert other classes as strings rather than actual class objects.
But strings will work nice for objects defined on a module top-level, since they can be fetched as a global variable - but won't work if you are using your cyclic class inside a function or method. A callable that will return the non-local variable with the instance name could work:
from types import FunctionType
classCyclicClass:
def__init__(self, name, next_item=None):
self.name = name
self.next_item = next_item
defprint_next(self):
print(self.next_item)
@propertydefnext_item(self):
ifisinstance(self._next_item, FunctionType):
self._next_item = self._next_item()
return self._next_item
@next_item.setterdefnext_item(self, value):
self._next_item = value
And testing on the interactive interpreter:
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'
But that approach is rather naive - and won't work if you re putting yur isntances into a list or other data-structures, unless you have a good timing triggering the attribute rendering into a real reference - at which point it is just better to allow late assignment to next_item anyway.
Not that if the "name" attributes are meant to be unique, you could modify the code above to have a global-registry of all your instances, and pass a string to identify your instances. That might suit your needs - but still will add more complications than allowing a late setting of next_item
cyclic_names = {}
classCyclicClass:
...
@propertydefnext_item(self):
ifisinstance(self._next_item, str):
self._next_item = cyclic_names[self._next_item]
return self._next_item
@next_item.setterdefnext_item(self, value):
self._next_item = value
@propertydefname(self):
return self._name
@name.setter(self)defname(self, value):
ifhasattr(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]
As you can see, the complexity for doing this work properly escalates quickly, and it may be a source of defects in your project - but still can be done if one thinks up of all nuances. (I'd use weakrefs on the global object index, for example)
Post a Comment for "Create Circular References Between Class Instances?"