Home > Mobile >  What is the pythonic way to have objects that reference one another?
What is the pythonic way to have objects that reference one another?

Time:07-03

Let's say I have the following code.

class Person:
    def __init__(partner: Person)
       self.partner = partner

So, basically, we have a world of persons, and every person has one partner. Obviously this relationship is mutual: if person A has person B as a partner, then person B must have person A as a partner.

But how would you make this code work in practice? How would I even instantiate A or B? And should code like this be avoided so as one does not run into recursive issues? What is common practice?

CodePudding user response:

You can use a method, after instanciation, that would create the 2 links, note that this a reference to the other not a copy

class Person:
    def __init__(self, name):
        self.name = name
        self.partner = None

    def set_partner(self, other: 'Person'):
        self.partner = other
        other.partner = self  

    def __str__(self):
        if self.partner:
            return self.name   "<>"   self.partner.name
        return self.name   " is alone"
a = Person("A")
b = Person("B")
print(a)  # A is alone
print(b)  # B is alone

a.set_partner(b)
print(a)  # A<>B
print(b)  # A<>B

You can't use other.set_partner(self) because that would then call again the method again and again, leading to a RecursionError

CodePudding user response:

There may be better ways to model this than having the Person object contain a reference to partner, but if you do need circular references, the solution is to set them up after the objects have been instantiated:

class Person:
    def __init__(self):
       self.partner = None


alice = Person()
bob = Person()
alice.partner = bob
bob.partner = alice

A way to do this that didn't involve circular references between the objects themselves might be to store the relationships somewhere else:

from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
    name: str

partners: dict[Person, Person] = {}

alice = Person("Alice")
bob = Person("Bob")

partners[alice] = bob
partners[bob] = alice

CodePudding user response:

Addition to @azro's answer, but now it avoids having one-sided couples.

class Person:
    def __init__(self, name):
        self.name = name
        self.partner = None

    def set_partner(self, other: 'Person'):
        if self.partner is not None:      # |this is new
            self.partner.partner = None   # |
        if other.partner is not None:     # |
            other.partner.partner = None  # |
        self.partner = other
        other.partner = self  

    def __str__(self):
        if self.partner:
            return self.name   "<>"   self.partner.name
        return self.name   " is alone"

a = Person("A")
b = Person("B")
c = Person("C")
print(a)  # A is alone

a.set_partner(b)
b.set_partner(c)
print(a)  # A is alone again
print(b)  # B<>C
print(c)  # C<>B
  • Related