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?"