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)]
我想澄清一些关于 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)]