使用python描述符时的常见设计模式是让描述符使用该描述符保留实例字典。例如,假设我想创建一个计算其访问次数的属性:
class CountingAttribute(object):
def __init__(self):
self.count = 0
self.value = None
class MyDescriptor(object):
def __init__(self):
self.instances = {} #instance -> CountingAttribute
def __get__(self, inst, cls):
if inst in self.instances:
ca = self.instances[inst]
else:
ca = CountingAttribute()
self.instances[inst] = ca
ca.count += 1
return ca
class Foo(object):
x = MyDescriptor()
def main():
f = Foo()
f.x
f.x
print("f.x has been accessed %d times (including the one in this print)"%(f.x.count,))
if __name__ == "__main__":
main()
这是一个完全愚蠢的例子,没有做任何有用的事情;我试图孤立主要观点。
问题是我无法在一个不可清除的类中使用此描述符,因为该行
self.instances[inst] = ca
使用实例作为字典键。处理这种情况有没有明智的方法?例如,一个人立即想要使用实例id
,但我不确定这样做是否会破坏应该如何使用哈希。
编辑:我意识到instances
应该像weakref.WeakKeyDictionary
一样,但我试图在这里保持简单,重点关注可持续性问题。
答案 0 :(得分:3)
您可以使用id(inst)
作为密钥。
请注意,这并不包括销毁对象并使用新ID创建新对象的情况。
为了正确检测到这一点,您应该将ca
和存储在字典中。如果您检测到weakref的引用对象已消失,则必须假设重用给定的id。
像
这样的东西import weakref
class MyDescriptor(object):
def __init__(self):
self.instances = {} #instance -> CountingAttribute
def __get__(self, inst, cls):
if inst is None: return self.instances # operating on the class, we get the dictionary.
i = id(inst)
if i in self.instances:
ca, wr = self.instances[i]
if wr() is None: del self.instances[i]
if i not in self.instances:
ca = CountingAttribute()
self.instances[i] = (ca, weakref.ref(inst))
ca.count += 1
return ca
这缓解了WeakKeyDictionary
所带来的可靠性问题。
但也许你根本不需要这个词。
可能是一种完全不同的方法class MyDescriptor(object):
def __get__(self, inst, cls):
if inst is None: return self, cls
try:
ca = inst.__the_ca
except AttributeError:
ca = inst.__the_ca = CountingAttribute()
ca.count += 1
return ca
这种方法也有其缺点。例如,您不能在类中多次使用描述符而不会使其变得丑陋。因此,只应谨慎使用。第一种解决方案虽然更复杂,但却是最简单的解决方案。