title: Implementing a bidirectional association relationship in Python
body: <p>In Martin Fowler's, UML Distilled, in the &quot;Bidirectional Association&quot; section, he says:</p>
  <blockquote>
  <p>Implementing a bidirectional association in a programming language is often a little tricky because
  you have to be sure that both properties are kept synchronized. Using C#, I use code along these
  lines to implement a bidirectional association:</p>
  </blockquote>
  <p><em>Code from the book</em></p>
  <pre class="lang-cs prettyprint-override"><code>class Car...
  public Person Owner {
  get {return _owner;}
  set {
  if (_owner != null) _owner.friendCars().Remove(this);
  _owner = value;
  if (_owner != null) _owner.friendCars().Add(this);
  }
  }
  private Person _owner;
  ...
  
  class Person ...
  public IList Cars {
  get {return ArrayList.ReadOnly(_cars);}
  }
  public void AddCar(Car arg) {
  arg.Owner = this;
  }
  private IList _cars = new ArrayList();
  internal IList friendCars() {
  //should only be used by Car.Owner
  return _cars;
  }
  ....
  </code></pre>
  <p><strong>Question 1:</strong></p>
  <p>I tried implementing this in python (<code>get_cars()</code> in <code>Person</code> &amp;&amp; <code>get_owner_v2()</code> in Car), I want to know if my code can be used to describe a 'bidirectional association' or not, and if not, how should it be modified to do so?</p>
  <p>Note: the first version (inspecting the caller's class/object) was working fine until I started creating car object independently and assigning them to owner on two steps (last 4 print statement proves that). The second version uses the licence numeber <code>lno</code> to figure out the owner. Not sure if I did it correctly but it does the work, based on my understanding.</p>
  <p>My Implementation:</p>
  <pre class="lang-py prettyprint-override"><code>#trying the inverse-bidirectional association
  
  class Person:
      cars_and_lnos = [] 
      def __init__(self,name):
          self.__cars = []
          self.__cars.append(&quot;Dummy&quot;)
          self.__name = name
  
      def add_car(self, *args, obj = None):
          # import inspect
          if not obj:
              car_object = Car(*args)
          else:
              car_object = obj
          Person.cars_and_lnos.append((car_object,self.__name))
          self.__cars.append(car_object)
      
      def __repr__(self):
          return f&quot;{self.__name}&quot;
  
      def get_cars(self):
          return self.__cars
  
          
  
  class Car:
      car_count = 0 
      def __init__(self, lno, price ,year, make, model):
          import inspect
          self.__lno = lno
          self.__price = price
          self.__year = year
          self.__make = make
          self.__model = model
          Car.car_count += 1
          self.__car_id = Car.car_count 
  
          if &quot;self&quot; in inspect.getargvalues(inspect.stack()[1][0]).args :
              self.__owned_by = f&quot;Car (ID:{self.__car_id}) is Owned By: {inspect.stack()[1][0].f_locals['self']}, which is an instance of Class: {inspect.stack()[1][0].f_locals['self'].__class__.__name__}&quot;
          else:
              self.__owned_by = &quot;This car is not owned by anyone.&quot;
      
      
      def __repr__(self):
          return f&quot;Car ID: {self.__car_id}.&quot;
  
      def get_specs(self):
          print(f&quot;+{'-'*30}+&quot;)
          print(f&quot;&quot;&quot;
      Liscense No.: {self.__lno}
      Price: {self.__price}
      Year: {self.__year}
      Make: {self.__make}
      Model: {self.__model}
      
          &quot;&quot;&quot;)
      
      @property
      def get_lno(self):
          return self.__lno
      
      def get_owner_v1(self):
          # import inspect
          return self.__owned_by
      
      def get_owner_v2(self):
          if Person.cars_and_lnos:
              for tup in Person.cars_and_lnos:
                  if self.__lno == tup[0].get_lno:
                      return f&quot;Car (ID: {self.__car_id}) is owned by: {tup[1]}, he is a: {Person.__name__} Class.&quot;
              return &quot;[0] This car is not owned by anyone.&quot;
          else:
              return &quot;[1] This car is not owned by anyone.&quot;
  
              
          
  
  owner1 = Person(&quot;William&quot;)    
  
  owner1.add_car(&quot;4567781&quot;,10000,2012,&quot;Toyota&quot;,&quot;Corrolla&quot;)
  owner1.add_car(&quot;2137813&quot;,8000,2010,&quot;Porshe&quot;,&quot;GT3&quot;)
  owner1.get_cars()[1].get_owner_v1()
  print(f&quot;{owner1} owns {len(owner1.get_cars())-1} Car(s).&quot;)
  print(owner1.get_cars()[1].get_owner_v1())
  print(&quot;=====================================================&quot;)
  
  
  owner2 = Person(&quot;Defoe&quot;)    
  owner2.add_car(&quot;8729120&quot;,10000,2012,&quot;Dodge&quot;,&quot;Challenger&quot;)
  print(f&quot;{owner2} owns {len(owner2.get_cars())-1} Car(s).&quot;)
  print(owner2.get_cars()[1].get_owner_v1())
  print(&quot;=====================================================&quot;)
  
  car1 = Car(&quot;7839291&quot;,10000,2012,&quot;Chevrolet&quot;,&quot;Camaro&quot;)
  car2 = Car(&quot;6271531&quot;,10000,2012,&quot;Ford&quot;,&quot;Mustang&quot;)
  print(car2.get_owner_v1())
  
  print(&quot;=====================================================&quot;)
  
  owner3 = Person(&quot;Lyan&quot;)
  owner3.add_car(&quot;656721&quot;,9000,2013,&quot;Toyota&quot;, &quot;Camry&quot;)
  owner3.add_car(&quot;652901&quot;,9000,2013,&quot;Nissan&quot;, &quot;Sunny&quot;)
  owner3.add_car(&quot;870251&quot;,9000,2013,&quot;BMW&quot;, &quot;6 Series&quot;)
  
  print(owner3.get_cars()[1].get_owner_v2())
  print(owner2.get_cars()[1].get_owner_v2())
  print(owner1.get_cars()[1].get_owner_v2())
  print(&quot;=====================================================&quot;)
  
  car3 = Car(&quot;5424201&quot;,10000,2012,&quot;Volks&quot;,&quot;Eos&quot;)
  
  print(car3.get_owner_v1())
  print(car3.get_owner_v2())
  
  owner4 = Person(&quot;Daphne&quot;)
  owner4.add_car(obj=car3)
  
  print(car3.get_owner_v1())
  print(car3.get_owner_v2())
  </code></pre>
  <p><strong>Question 2:</strong></p>
  <p>In this section he says the that these two notations are likely the same:</p>
  <p><a href="https://i.stack.imgur.com/0BDlo.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/0BDlo.png" alt="enter image description here" /></a></p>
  <p><a href="https://i.stack.imgur.com/YMs5J.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/YMs5J.png" alt="enter image description here" /></a></p>
  <p>Is the following notation (without any arrows specified) can also be considered bidirectional relationship?</p>
  <p><a href="https://i.stack.imgur.com/cSexD.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/cSexD.png" alt="enter image description here" /></a></p>
  <p><strong>EDIT:</strong></p>
  <p>I understand that a logical refinement to the class diagram would be * to * (many to many), but I'm only concerned about how correct my implementation to the description/design is and how can it be improved, and where the no-arrows association line fit in the picture.</p>