Python: 描述符的缓存值

Python: Cache values of descriptors

我想澄清一些关于 Python 的描述符的事情。我想使用一些复杂的 set/get 机制将 属性 添加到我的 class 并将这些计算值缓存在描述符对象中。一个简化的示例如下所示:

class Pro(object):
    """My descriptor class"""

    def __init__(self):
        self.value = None

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = value


class Something(object):
    """My simple class"""
    pro = Pro()

a = Something()
a.pro = 1

b = Something()
b.pro = 2

print(a.pro, b.pro) # At first, I've expected them to be 1 and 2

我以某种方式认为 pro 属性对于 Something 的每个实例都是 Pro 的唯一实例,显然我错了。看起来我应该在 __set____get__ 中使用 instance._value 而不是 self.value,但我真的希望将所有内容隐藏在 Pro [=26] 中=].这可能吗?谢谢!

您的代码存在的问题是您在 Pro 的实例上设置了将由 Something 的所有实例共享的属性。要解决此问题,您应该在 Something 的单个实例上设置一个属性,一种方法是使用元类:

class Meta(type):
    def __new__(cls, name, bases, dct):
        for k, v in dct.items():
            if isinstance(v, Pro):
                # add an _ in front of the name
                v.name = '_' + k
        return super(Meta, cls).__new__(cls, name, bases, dct)


class Pro(object):

    def __get__(self, ins, typ):
        return getattr(ins, self.name)

    def __set__(self, ins, val):
            setattr(ins, self.name, val)

class Something(object):
    """My simple class"""
    __metaclass__ = Meta
    pro = Pro()

a = Something()
a.pro = 1

b = Something()
b.pro = 2

演示:

>>> a.pro, b.pro
(1, 2)
>>> a.__dict__
{'_pro': 1}
>>> b.__dict__
{'_pro': 2}
>>> a.pro = 100
>>> a.__dict__
{'_pro': 100}

So there is no way around making hidden attributes in Something instances, right?

不,有。您可以在 Pro 的实例中存储一个字典,该实例存储与 Something 的每个实例相关的所有值。例如,如果 Something 的实例是可散列的,那么您可以使用 weakref.WeakKeyDictionary 执行类似的操作。 WeakKeyDictionary 将确保一旦 Something 的实例没有剩余引用,那么它会立即被垃圾收集,这对于正常的 dict:

是不可能的
from weakref import WeakKeyDictionary

class Pro(object):

    def __init__(self):
        self.instances = WeakKeyDictionary()

    def __get__(self, ins, typ):
        return self.instances[ins]

    def __set__(self, ins, val):
        self.instances[ins] = val

p = Pro()

class Something(object):
    """My simple class"""
    pro = p

a = Something()
a.pro = 1

b = Something()
b.pro = 2

print a.pro, b.pro

print p.instances.items()
del a
print p.instances.items()

输出:

1 2
[(<__main__.Something object at 0x7fb80d0d5310>, 1), (<__main__.Something object at 0x7fb80d0d5350>, 2)]
[(<__main__.Something object at 0x7fb80d0d5350>, 2)]