I want to a method that runs when object reference count is changed. is there a method like the following code ?
class Foo():
def __method__(self):
print("ojbect reference count is changed !")
a = Foo()
b = a # runs __method__ because the reference count is increased
c = b # runs __method__ because the reference count is increased
del b # runs __method__ because the reference count is decreased
del c # runs __method__ because the reference count is decreased
Note: This is a repost of this question, I'm looking for an answer compatible with Python 3.10.
Thanks!
The reference count of my object never goes below 1. Taking this as a given, I tried to implement some clean-up code for when an object is dereferenced by all user-facing code by adding __del__
. That didn't work, because there's one reference left.
So, I need to add some clean-up code for the reference count hits 1.
This is an XY Problem, and I'm comfortable with that. My actual situation involves singletons, wherein ((User("my_name") == User("my_name")) and (User("my_name") != User("other_name"))) is True
. To do this, there's an internally referenced dictionary that's handling all this. That's why the minimum reference count for an object is 1.
When an object is removed from the User's code, ie, the reference count reaches 1, I'd like to clean it up from my reference dictionary as well.
I'm afraid there's no magic method like that in Python. However, if you're looking to implement a singleton with auto-cleanup, using a WeakValueDictionary
would work:
from __future__ import annotations
from threading import Lock
from weakref import WeakValueDictionary
_USERS_LOCK = Lock()
_USERS: WeakValueDictionary[str, User] = WeakValueDictionary()
class User:
def __new__(self, name: str) -> User:
with _USERS_LOCK:
# Try to return an existing user.
try:
user = _USERS[name]
except KeyError:
# Create and cache a new user.
user = _USERS[name] = object.__new__(User)
user.name = name
# All done!
return user
You can test this works as expected like so:
# Users created with a unique username return new user objects.
user1 = User("Dave")
assert user1.name == "Dave"
user2 = User("TheOnlyWayUp")
assert user2.name == "TheOnlyWayUp"
assert user1 is not user2
# The users are cached.
assert len(_USERS) == 2
# Users created with the same username as an existing user return
# the same user instance.
user3 = User("Dave")
assert user3 is user1
# The number of cached users remains the same.
assert len(_USERS) == 2
# Deleting the users frees up the cache.
del user1, user2, user3
assert len(_USERS) == 0
See: