2

I have two classes, B and C.

I want to instantiate B with C reference and C with B reference.

I could add a setter method, but was wondering if I can do it in the __init__ stage or any other elegant ways

wjandrea
  • 28,235
  • 9
  • 60
  • 81
ender maz
  • 91
  • 1
  • 8

2 Answers2

2

It is not possible within __init__ directly due to a chicken and egg situation. However, it is possible in one assignment statement:

>>> class A:
...     pass
... 
>>> class B:
...     pass
... 
>>> a, b = b.a, a.b = A(), B()
>>> a.b is b
True
>>> b.a is a
True

This relies on the fact that Python evaluates assignments left to right.

It is not thread safe; if you need to guarantee that the references exist in a threaded application then you'll want to use a mutex to handle the possible race conditions. The GIL works at the opcode level, which is a finer-grained resolution than lines of Python code.

Community
  • 1
  • 1
wim
  • 338,267
  • 99
  • 616
  • 750
  • It also does not pass the references to both constructors simultaneously, which is of course impossible. – Josh Lee Apr 26 '17 at 20:24
  • 2
    @JoshLee that's splitting hairs to an absurd degree. Even atomic operations don't do two things simultaneously. – Adam Smith Apr 26 '17 at 20:25
  • @AdamSmith Just to clarify. The specific question asked is not possible, but there are various creative ways to answer it. – Josh Lee Apr 26 '17 at 20:28
1

You could do it in __init__ if you make one of the class initializers take an object of the other class:

>>> class B:
...    def __init__(self):
...       self.c = C(self)
...
>>> class C:
...    def __init__(self, b):
...       self.b = b
...
>>> b = B()
>>> c = b.c
>>> b.c
<__main__.C object at 0x107a4f6d8>
>>> b.c.b.c
<__main__.C object at 0x107a4f6d8>
>>> b.c.b.c.b
<__main__.B object at 0x107a60e80>
>>> b
<__main__.B object at 0x107a60e80>
>>> c
<__main__.C object at 0x107a4f6d8>
>>> c.b
<__main__.B object at 0x107a60e80>
>>> b.c
<__main__.C object at 0x107a4f6d8>
>>> b.c.b.c
<__main__.C object at 0x107a4f6d8>
>>> c.b.c.b
<__main__.B object at 0x107a60e80>

Or even without any arguments to __init__:

>>> class B:
...     def __init__(self):
...         self.c = C()
...         self.c.b = self
...
>>> class C:
...     pass
...
>>> b = B()
>>> c = b.c
>>> b
<__main__.B object at 0x10835c048>
>>> c
<__main__.C object at 0x1085ccac8>
>>> b.c
<__main__.C object at 0x1085ccac8>
>>> c.b
<__main__.B object at 0x10835c048>
>>> b.c.b.c.b
<__main__.B object at 0x10835c048>
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172