如何在Python中使两个对象具有相同的ID?

7

If I have a class like below:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

同时有两个对象:

a = Point(1,2)
b = Point(1,2)

我该如何修改Point类以使得id(a) == id(b)成立?

3
唯一的方法是使它们成为同一个对象。为什么你想要这样做? - BrenBarn
唯一的方法是拥有两个指向同一对象的引用。 - Tapan Chandra
3个回答

9
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

当你需要一个非常简单的类,例如 Point 类时,应该考虑使用 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

我使用了一个函数和namedtuple一起,因为在我看来它更简单,但是如果需要的话,你可以很容易地将它表示为一个类:

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

正如 @PetrViktorin 在他的回答中指出的那样,你应该考虑使用weakref.WeakValueDictionary,因为当类的实例被删除时(显然不适用于namedtuple),它们在字典本身中仍然被引用,所以它们不会保留在内存中。


1
啊,享元模式。如果有人子类化了“Point”,为避免混淆,你应该将缓存命名为“__cache”。 - John La Rooy
+1 我以前不知道 __new__,我一直在使用 __init__ 的方法。现在我知道为什么它失败了。 - Ashwini Chaudhary

6
你需要拥有一个全局对象字典,并通过工厂函数(或自定义的__new__,参见其他答案)获取它们。此外,考虑使用WeakValueDictionary,以便不会将不再需要的对象占用内存。
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))

请注意,命名元组与弱引用字典(WeakValueDictionary)不兼容。

2
如果您需要比较两个对象是否包含相同的值,您可以使用eq运算符
>>> 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接