我想澄清一些关于Python描述符的事情。我想通过一些复杂的set / get机制向我的类添加一个属性,并在描述符对象中缓存这些计算值。一个简化的例子如下所示:
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
的每个实例,Pro
属性都是Something
的唯一实例,显然我错了。看起来我应该在instance._value
和self.value
内使用__set__
而不是__get__
,但我真的希望隐藏Pro
类内的所有内容。这甚至可能吗?谢谢!
答案 0 :(得分:3)
您的代码存在的问题是,您要在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}
因此无法在Something中创建隐藏属性 实例,对吧?
不,有。您可以在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)]