Rick
Rick

Reputation: 45281

Tracking references to object instances

This is more or less how I would track the number of class instances, since __new__ is called every time one is made:

class MyClass():
    def __new__(klass):
        try:
            klass.__instances = klass.__instances + 1
        except NameError:
            klass.__instances = 1
        return super(MyClass,klass).__new__(klass)

Is there a magic method that is called when a new reference to a specific instance of a class is made? If not, is there a straight forward way to implement one? For example:

class MyClass():
    def __init__(self):
        self.__ref = 1
        print(self,"created.")
    def __new_reference(self):
        self.__ref = self.__ref + 1
        print("Reference to",self,"added.")
    def __del_reference(self):
        self.__ref = self.__ref - 1
        print("Reference to",self,"deleted.")

So now:

L1 = []
L2 = []
L1.append(MyClass()) #<MyClass object> created
L1[0].__ref          #1
L2.append(L1[0])     #Reference to <MyClass object> added.
L2[0].__ref          #2
L1.pop(0)            #Reference to <MyClass object> deleted.
L2[0].__ref          #1

Edit:

Here's the problem I thought I would try to solve using reference tracking.

My idea is to have an object A instance that contains multiple (weak) references to object Bs. There is a separate list containing all valid Bs. There is a list of all the As as well (thousands). The desired behavior is that when any one B is removed from the B list, for any object A to become None if it contained a reference to that particular B removed from the B list.

Upvotes: 1

Views: 634

Answers (2)

Rick
Rick

Reputation: 45281

I just thought of a simple way to almost accomplish this, and it's probably good enough for my situation.

All I have to do is be sure to call the object every time it is referenced. This isn't quite as neat and tidy as I was hoping for, but it does allow tracking of the number of references that I'm interested in (though it does not track all references).

Here's a code sample to illustrate:

class MyClass():
    def __init__(self):
        self._ref = 1
        print(self,"created.")
    def __call__(self, new_ref=True):
        if new_ref is True: self.__new_reference()
        elif new_ref is False: self.__del_reference()
        return self
    def __new_reference(self):
        self._ref = self._ref + 1
        print("Reference to",self,"added.")
    def __del_reference(self):
        self._ref = self._ref - 1
        print("Reference to",self,"deleted.")

Now I can do this:

L1 = []
L2 = []
L1.append(MyClass())   #<MyClass object> created.
print(L1[0]._ref)      #1
L2.append(L1[0](True)) #Reference to <MyClass object> added.
print(L2[0]._ref)      #2
L1.pop(0)(False)       #Reference to <MyClass object> deleted.
print(L2[0]._ref)      #1

I just have to remember to call my special object every time I add or delete it.

Alternatively, I could subclass a special container class for the special objects that calls the object with the appropriate argument when it is added or removed. Not sure if it's worth the effort to do this though.

Upvotes: 0

arshajii
arshajii

Reputation: 129517

As far as I know there is no magic method that does this, but maybe you could use sys.getrefcount():

Return the reference count of the object. The count returned is generally one higher than you might expect, because it includes the (temporary) reference as an argument to getrefcount().

Using your example:

>>> import sys
>>> class MyClass: pass
... 
>>> L1 = []
>>> L2 = []
>>> L1.append(MyClass())
>>> sys.getrefcount(L1[0])
2
>>> L2.append(L1[0])
>>> sys.getrefcount(L1[0])
3
>>> del L1[0]
>>> sys.getrefcount(L2[0])
2

Upvotes: 2

Related Questions