Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make two objects have the same id in python?

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)?

like image 726
amazingjxq Avatar asked Jun 07 '13 05:06

amazingjxq


3 Answers

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.

like image 63
jamylak Avatar answered Sep 23 '22 18:09

jamylak


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.

like image 21
Petr Viktorin Avatar answered Sep 25 '22 18:09

Petr Viktorin


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
like image 29
invert Avatar answered Sep 22 '22 18:09

invert