设C为Python类,并假设C的构造函数将整数作为参数。
现在考虑说明
x = C(0)
y = C(0)
Python的默认行为意味着x和y在内存中占据两个不同的位置。
是否可以强制x和y在内存中共享相同的位置?
如果有一些Python装饰工作,我会非常高兴。
[注意] 我正在寻找一种记忆构造函数的方法(有关函数的记忆,请参阅http://en.wikipedia.org/wiki/Memoization。)
[添加] Sage开源数学软件通过课程UniqueRepresentation
为此问题提供了一个非常好的解决方案(参见here)。任何类都应该继承此类以具有预期的行为。不过,我想知道是否有一个纯Python解决方案来解决这个问题。
答案 0 :(得分:2)
您可能想要使用lru_cache。如果您的班级定义是
@lru_cache(maxsize=32)
class C(object):
def __init__(self, num):
self.num = num
然后它表现得像
>>> a = C(1)
>>> a.num = 2
>>> b = C(1)
>>> b.num
2
>>> a is b
True
但是,这使名称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
我们给__new__
所有可能的参数和关键字参数,以便它也可以与其他类一起使用。现在我们可以像这样缓存类C2
的实例:
@lru_cache_class(maxsize=32)
class C2(object):
def __init__(self, num):
self.num = num
我们可以看到对象被缓存:
>>> c = C2(2)
>>> c is C2(2)
True
然而,与第一种方法相比,这种方法存在另一个细微差别。例如:
>>> d = C2(3)
>>> d.num = 4
>>> d.num
4
>>> e = C2(3)
>>> d.num == e.num
>>> d.num
3
此行为是预期的,因为无论如何都会调用__init__
,尽管对象的内存位置保持不变。根据您的使用情况,您可能还想缓存__init__
的输出。
答案 1 :(得分:1)
您可以覆盖__new__
来存储每个对象的缓存版本:
class C(object):
_cache = {}
def __new__(cls, x):
if x not in C._cache:
C._cache[x] = object.__new__(cls, x)
return C._cache[x]
def __init__(self, x):
self.x = x
演示:
>>> a = C(1)
>>> b = C(1)
>>> a is b
True
>>> id(a) == id(b)
True
显然,如果您稍后更改x
而不是创建新类,则它将不会成为与之前使用x
的值定义的对象相同的对象:
>>> a = C(1)
>>> b = C(2)
>>> a.x = 2
>>> a is b
False
答案 2 :(得分:0)
如果您愿意让函数为您创建类实例,这可能会有效。假设你的班级C
接受一个整数:
def C_getter(num, _class_archive={}):
"""\
Returns an instance of the `C` class,
making sure that if an object already exists with that
integer number a new object is not created.
The _class_archive is used to keep a record of all the instances
in memory local to this function. Don't actually supply an
argument to _class_archive when you call this function.
"""
if num not in _class_archive:
_class_archive[num] = C(num)
return _class_archive[num]
像这样使用:
>>> a = C_getter(0)
>>> b = C_getter(0)
>>> a is b
True
>>> c = C(0)
>>> a is c
False
我正在利用这样一个事实:如果你使用一个可变对象作为一个函数的默认参数,每次调用该函数时都会使用相同的可变对象。
修改强>
如果您想使这个通用(假设您的所有类都需要一个数字),您可以执行以下操作:
def getter(your_class, num, _class_archive={}):
if (your_class, num) not in _class_archive:
_class_archive[(your_class, num)] = your_class(num)
return _class_archive[(your_class, num)]
你可以像这样使用它:
>>> a = getter(C, 0)
>>> b = getter(C, 0)
>>> c = getter(A, 0)