Python 对象的独特表示

Sam*_*udo 3 python constructor caching object decorator

假设 C 是一个 Python 类,并假设 C 的构造函数采用整数作为参数。

现在考虑说明

x = C(0)
y = C(0)
Run Code Online (Sandbox Code Playgroud)

Python 的默认行为意味着 x 和 y 在内存中占据两个不同的位置。

是否可以强制 x 和 y 共享内存中的同一位置?

如果某个 Python 装饰器能够完成这项工作,我会非常高兴。

[注意]我正在寻找一种记忆构造函数的方法(有关函数记忆的信息,请参阅http://en.wikipedia.org/wiki/Memoization )。

【补充】 Sage开源数学软件通过类为这个问题提供了非常好的解决方案UniqueRepresentation(参见这里)。任何类都应该从该类继承以获得预期的行为。尽管如此,我想知道是否有一个纯Python的解决方案来解决这个问题。

jas*_*rim 5

您可能想使用lru_cache。如果你的类定义是

@lru_cache(maxsize=32)
class C(object):
    def __init__(self, num):
        self.num = num
Run Code Online (Sandbox Code Playgroud)

那么它的行为就像

>>> a = C(1)
>>> a.num = 2
>>> b = C(1)
>>> b.num
2
>>> a is b
True
Run Code Online (Sandbox Code Playgroud)

然而,这使得该名称成为C一个函数,并且在该类实际实例化之前,任何类功能都无法使用。如果需要,也可以直接缓存__new__负责创建对象的方法。 是一个方法,它采用与我们之前创建类实例时__new__调用的所有相同的参数。__init____init__

由于缓存输出__new__很简单,我们可以让事情变得更有趣。让我们创建一个新的装饰器,它的工作方式与 类似lru_cache,但它可以与类一起使用来缓存 的输出__new__

def lru_cache_class(maxsize):
    def wrap(klass):
        @lru_cache(maxsize=maxsize)
        def new(cls, *args, **kwargs):
            self = object.__new__(cls)
            return self
        klass.__new__ = new
        return klass
    return wrap
Run Code Online (Sandbox Code Playgroud)

我们给出__new__所有可能的参数和关键字参数,以便它也可以与其他类一起使用。C2现在我们可以像这样缓存类的实例:

@lru_cache_class(maxsize=32)
class C2(object):
    def __init__(self, num):
        self.num = num
Run Code Online (Sandbox Code Playgroud)

我们可以看到对象被缓存了:

>>> c = C2(2)
>>> c is C2(2)
True
Run Code Online (Sandbox Code Playgroud)

然而,与第一种方法相比,这种方法还有另一个细微的差别。例如:

>>> d = C2(3)
>>> d.num = 4
>>> d.num
4
>>> e = C2(3)
>>> d.num == e.num
>>> d.num
3
Run Code Online (Sandbox Code Playgroud)

此行为是预期的,因为__init__尽管对象的内存位置保持不变,但无论如何都会被调用。根据您的用例,您可能还想缓存输出__init__

  • 你说得对。我刚刚编辑了我的答案以显示另一种方式,它将名称“C”保留为一个类。 (2认同)