If I have a class like below:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
And have 2 objects:
a = Point(1,2)
b = Point(1,2)
How can I modify class Point to make id(a) == id(b)
?
class Point(object):
__cache = {}
def __new__(cls, x, y):
if (x, y) in Point.__cache:
return Point.__cache[(x, y)]
else:
o = object.__new__(cls)
o.x = x
o.y = y
Point.__cache[(x, y)] = o
return o
>>> Point(1, 2)
<__main__.Point object at 0xb6f5d24c>
>>> id(Point(1, 2)) == id(Point(1,2))
True
When you need a really simple class like Point
, always consider collections.namedtuple
from collections import namedtuple
def Point(x, y, _Point=namedtuple('Point', 'x y'), _cache={}):
return _cache.setdefault((x, y), _Point(x, y))
>>> Point(1, 2)
Point(x=1, y=2)
>>> id(Point(1, 2)) == id(Point(1, 2))
True
I used a function alongside namedtuple
because it is simpler IMO but you can easily represent it as a class if needed:
class Point(namedtuple('Point', 'x y')):
__cache = {}
def __new__(cls, x, y):
return Point.__cache.setdefault((x, y),
super(cls, Point).__new__(cls, x, y))
As @PetrViktorin noted in his answer you should consider the use of a weakref.WeakValueDictionary
so deleted instances of the class (doesn't work with namedtuple
apparently) don't remain in memory since they remain referenced in the dictionary itself.
You need to have a global dictionary of objects, and get them through a factory function (or a custom __new__
, see the other answers). Additionally, consider using a WeakValueDictionary
so that you don't unnecessarily fill up memory with objects that are no longer needed.
from weakref import WeakValueDictionary
class _Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
# Cache of Point objects the program currently uses
_points = WeakValueDictionary()
def Point(x, y):
"""Create a Point object"""
# Note that this is a function (a "factory function")
# You can also override Point.__new__ instead
try:
return _points[x, y]
except KeyError:
_points[x, y] = point = _Point(x, y)
return point
if __name__ == '__main__':
# A basic demo
print Point(1, 2)
print id(Point(1, 2))
print Point(2, 3) == Point(2, 3)
pt_2_3 = Point(2, 3)
# The Point(1, 2) we created earlier is not needed any more.
# In current CPython, it will have been been garbage collected by now
# (but note that Python makes no guarantees about when objects are deleted)
# If we create a new Point(1, 2), it should get a different id
print id(Point(1, 2))
Note that a namedtuple won't work with WeakValueDictionary.
If you need to compare whether your two objects house the same values, you can implement the eq operator:
>>> class Point(object):
... def __init__(self, x, y):
... self.x = x
... self.y = y
... def __eq__(self, other):
... return self.x == other.x and self.y == other.y
...
>>> a = Point(1,2)
>>> b = Point(1,2)
>>> a == b
True
>>> b = Point(2,2)
>>> a == b
False
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With