在不可用的类中使用描述符 - python

Dan*_*ank 5 python descriptor

使用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()
Run Code Online (Sandbox Code Playgroud)

这是一个完全愚蠢的例子,没有做任何有用的事情; 我试图孤立主要观点.

问题是我不能在一个不可清除的类中使用这个描述符,因为该行

self.instances[inst] = ca
Run Code Online (Sandbox Code Playgroud)

使用实例作为字典键.处理这种情况有没有明智的方法?例如,一个人立即想要使用实例id,但我不确定这样做是否会破坏应该如何使用哈希.

编辑:我意识到instances应该是类似的东西,weakref.WeakKeyDictionary但我试图在这里保持简单,专注于可持续性问题.

glg*_*lgl 3

您可以用作id(inst)钥匙。

请注意,这不包括对象被销毁并使用新 id 创建新对象的情况。

为了正确检测到这一点,您应该在字典中存储ca 和弱引用。如果您检测到弱引用引用的对象已消失,则必须假设给定的 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
Run Code Online (Sandbox Code Playgroud)

这缓解了与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
Run Code Online (Sandbox Code Playgroud)

这种方法也有其缺点。例如,您不能轻松地在一个类中多次使用描述符而不使其变得丑陋。因此,应谨慎使用。第一个解决方案虽然更复杂,但却是最简单的一个。

  • 在第二个版本中,您可能明智地使用“__varname”私有变量语法,这样,如果您放入的类已经具有表示其他含义的“the_ca”属性,您就不会意外破坏事物。所以:`instance.__the_ca`。这正是 `__varname` 语法的用途! (2认同)