在Raymond Hettingers Mental Game on YouTube中:
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在Writing descriptors in Python 3.6+中说:
[...]而不是使用内置函数getattr和setattr,我们需要直接进入 dict 对象,因为内置描述符也将被描述符协议拦截并引起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 Describing Descriptors on 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
实现描述符的正确方法是什么?
答案 0 :(得分:1)
第一个例子很好。这三个都是有效的实现。
不确定第二作者为什么说您不能以这种方式使用get_graph
。是的,getattr
调用描述符协议,但是描述符已分配给getattr
,但是您将type(obj).__dict__[name]
设置为private_name
,所以不会有无限递归...如果您在f'_{name}'
中使用self.private_name = name
而不是__set_name__
,将会 ,但这不是前两个方法中的任何一个...
编辑:阅读该链接,这就是作者正在做的事情...
话虽这么说,第二个解决方案不是不正确。
关于第三个解决方案,我想这是一个替代方案,它根本不会污染实例名称空间,而是保留一个单独的名称空间-self.private_name = f'_{name}'
。这不是一个坏主意,但是与其他两个相比,它“没有更多的”正确性。请注意,它确实假定基于身份的类散列不是这种情况。您可以在类中实现WeakKeyDictionary
以便基于其他内容进行哈希处理,如果您的类“在概念上”是不可变的,例如一些__hash__
类,它不公开任何增变方法,并且基于Point(x, y)
和x
的值进行散列。因此,这将使该方法更具限制性,但是,否则,这是一个不弄乱实例名称空间的聪明解决方案。
我认为第一个是Python语言中最惯用的。但同样,这三个都是有效的解决方案。