在Python 3.6+中实现描述符的正确方法是什么



在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

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

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

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

编辑:阅读链接,这就是作者正在做的。。。

话虽如此,第二个解决方案并不是错误的。

至于第三种解决方案,我推测这是一种替代方案,它根本不会污染实例名称空间,保留一个单独的名称空间——WeakKeyDictionary。这不是一个坏主意,但它不再是";正确的";比其他两个。注意,它确实假设了基于身份的类哈希,但事实并非如此。你可以在一个类中实现__hash__,以基于其他东西进行哈希,如果你的类是";在概念上";不可变的,例如一些Point(x, y)类,它不公开任何赋值函数方法,并且基于xy的值进行散列。因此,这将使这种方法的限制性更强,但除此之外,它是一个不干扰实例命名空间的聪明解决方案。

我认为第一个是最Python的,因为它是地道的。但同样,这三个都是有效的解决方案。

相关内容

最新更新