3

在YouTube 上的Raymond Hettingers心理游戏中:

class Validator:

    def __set_name__(self, owner, name):
        self.private_name = f'_{name}'    

    def __get__(self, obj, objtype=None):
        return getattr(obj, self.private_name)

    def __set__(self, obj, value):
        self.validate(value)
        setattr(obj, self.private_name, value)

Daw-Ran Liou 在用 Python 3.6+ 编写描述符中指出:

[...] 我们需要直接访问dict对象,而不是使用内置函数 getattr 和 setattr,因为内置函数也会被描述符协议拦截并导致 RecursionError。

class Validator:

    def __set_name__(self, owner, name):
        self.name= name
    
    def __get__(self, obj, objtype=None):
        return obj.__dict__[self.name]

    def __set__(self, obj, value):
        self.validate(value)
        obj.__dict__[self.name] = value

但 Matthew Egans在 YouTube 上描述描述符时说:

from weakref import WeakKeyDictionary

class Validator:
    def __init__(self):
        self.data = WeakKeyDictionary()

    def __get__(self, obj, owner):
        return self.data[obj]

    def __set__(self, obj, value):
        self.validate(value)
        self.data[obj] = value

实现描述符的正确方法是什么?

4

1 回答 1

1

第一个例子很好。这三个都是有效的实现。

不知道为什么第二作者说你不能那样使用getattr。是getattr的,调用描述符协议,但描述符被分配给type(obj).__dict__[name]但你设置private_namef'_{name}'所以不会有任何无限递归......如果你使用self.private_name = namein__set_name__而不是self.private_name = f'_{name}',但这不是前两个中的任何一个是做...

编辑:阅读该链接,这就是作者正在做的事情......

话虽如此,第二种解决方案并不正确

至于第三种解决方案,我推测这是一种替代方案,它根本不会污染实例命名空间,保留一个单独的命名空间—— WeakKeyDictionary. 不错的主意,但它并不比其他两个更“正确”。请注意,它确实假设基于身份的类散列,但事实并非如此。你可以__hash__在一个类中实现基于其他东西的散列,如果你的类是“概念上”不可变的,这很好,例如一些Point(x, y)不公开任何突变方法的类,并且基于 and 的值进行x散列y。因此,这将使这种方法更具限制性,但除此之外,它是一个不弄乱实例命名空间的聪明解决方案。

我认为第一个是最符合 Pythonic 的,因为它是惯用的。但同样,这三个都是有效的解决方案。

于 2020-07-25T11:20:51.730 回答