Reputation: 29
I'm writing a class called Position, something like Python's Small Integer Constant Pool:
class Position:
"""Takes 2 args: x, and y. If coordinate are equal, make them pointing to the same object"""
def __init__(self, x, y):
self.x = x
self.y = y
def __new__(self, *args, **kwargs):
import gc
for obj in gc.get_objects():
if isinstance(obj, eval(self.__qualname__)): # Won't work cause arg 2 is a string, not a type name
if obj.x == self.x and obj.y == self.y:
return obj
return super(Position, self).__new__(self, *args, **kwargs) # Have no idea what to write
p1 = Position(1, 2)
p2 = Position(2, 3)
p3 = Position(1, 2)
print(p1 is p2, p2 is p3, p1 is p3) # Should print False, False, True
Rewriting hash and eq won't work when using is
to compare.
I want my class to be something like:
a = 1
b = 1
print(a is b) # True
Upvotes: 0
Views: 957
Reputation: 110301
You can't rely on the garbage collector list of all existing objects for this, and much less on a linear search of all objects.
Simply keep your objects in a registry where you can find then back.
You can use WeakValues dictionary so that if there is no reference left to anyone of your objects it is discarded. (Just use a plain dictionary if you want each one to remain created, even if it is no longer in use):
from weakref import WeakValueDictionary
class Position:
"""Takes 2 args: x, and y. If coordinate are equal, make them pointing to the same object"""
_registry = WeakValueDictionary()
def __new__(cls, x, y):
if (x,y) in cls._registry:
return cls._registry[x,y]
instance = super().__new__(cls) # don't pass extra *args and **kwargs to obj.__new__
cls._registry[x,y] = instance
return instance
def __init__(self, x, y):
if hasattr(self, "x"): # avoid running init twice if the attribute is already set
return
self.x = x
self.y = y
p1 = Position(1, 2)
p2 = Position(2, 3)
p3 = Position(1, 2)
in this scenario p1 == p3 evaluates to True
Upvotes: 2