在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)
类,它不公开任何赋值函数方法,并且基于x
和y
的值进行散列。因此,这将使这种方法的限制性更强,但除此之外,它是一个不干扰实例命名空间的聪明解决方案。
我认为第一个是最Python的,因为它是地道的。但同样,这三个都是有效的解决方案。