Saqib Ali
Saqib Ali

Reputation: 12585

How do I create "reverse links" amongst Python classes without circular imports?

Suppose I have a Python class called Person in file person.py as follows:

class Person(object):
    static_id = 0
    instance_id = None

    @staticmethod
    def new_id():
        Person.static_id += 1
        return Person.static_id

    def __init__(self):
        self.instance_id = Person.new_id()

    def __repr__(self):
        return "<Person #%s>" % (self.instance_id)

Instantiating Person works:

>>> a = Person()
>>> a
<Person #1>
>>> b = Person()
>>> b
<Person #2>

And I have another class called Thing in file thing.py as follows:

from person import Person

class Thing(object):
    static_id = 0
    instance_id = None
    instance_creator = None

    @staticmethod
    def new_id():
        Thing.static_id += 1
        return Thing.static_id

    def __init__(self, person):
        self.instance_id = Thing.new_id()
        self.instance_creator = person

    def __repr__(self):
        return "<Thing #%s created by %s>" % (self.instance_id, self.instance_creator)

Now, when I instantiate instances of Thing I see the following:

>>> Thing(person=b)
<Thing #1 created by <Person #2>>

>>> Thing(person=b)
<Thing #2 created by <Person #2>>

>>> Thing(person=a)
<Thing #3 created by <Person #1>>

How can I create a (non-static) method Person.created_things() that will return me all Things created by that instance of Person? I want:

>>> b.created_things()
[<Thing #1 created by <Person #2>>, <Thing #2 created by <Person #2>>]

But I cannot figure out how to do this without importing thing.Thing into person.py. Doing so would create a circular import reference. So how can I do it? In this case these are not Django classes.

Upvotes: 0

Views: 62

Answers (2)

PM 2Ring
PM 2Ring

Reputation: 55479

You can do this without modifying the Person definition in person.py by dynamically modifying the person instance passed to Thing:

from person import Person

class Thing(object):
    static_id = 0
    instance_id = None
    instance_creator = None

    @staticmethod
    def new_id():
        Thing.static_id += 1
        return Thing.static_id

    def __init__(self, person):
        self.instance_id = Thing.new_id()
        self.instance_creator = person
        try:
            person.created_things.append(self)
        except AttributeError:
            person.created_things = [self]

    def __repr__(self):
        return "<Thing #%s created by %s>" % (self.instance_id, self.instance_creator)


a = Person()
print a

b = Person()
print b

t1 = Thing(person=b)
print t1

t2 = Thing(person=b)
print t2

t3 = Thing(person=a)
print t3

print a.created_things
print b.created_things

output

<Person #1>
<Person #2>
<Thing #1 created by <Person #2>>
<Thing #2 created by <Person #2>>
<Thing #3 created by <Person #1>>
[<Thing #3 created by <Person #1>>]
[<Thing #1 created by <Person #2>>, <Thing #2 created by <Person #2>>]

Upvotes: 1

Eithos
Eithos

Reputation: 2491

Just add a couple of methods to Person (and initialize self.things in __init__):

def __init__(self):
    self.things = []
    self.instance_id = Person.new_id()

def addThing(self, thing):
    self.things.append(thing)

def created_things(self):
    return self.things

And when you initialize Thing, just add it to Person.

def __init__(self, person):
    self.instance_id = Thing.new_id()
    self.instance_creator = person
    self.instance_creator.addThing(self)

Upvotes: 1

Related Questions