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

在Python 3.6+;中实现描述符的正确方法是什么;?,python,python-3.6,python-descriptors,Python,Python 3.6,Python Descriptors,在Raymond Hettingers中: 类验证程序: 定义设置名称(自身、所有者、名称): self.private_name=f'{name}' 定义获取(self、obj、objtype=None): 返回getattr(对象,self.private\u名称) 定义设置(自身、对象、值): 自我验证(值) setattr(对象,self.private_名称,值) 刘道然表示: […]我们不需要使用内置函数getattr和setattr,而是需要直接访问dict对象,因为内置函数也会

在Raymond Hettingers中:

类验证程序:
定义设置名称(自身、所有者、名称):
self.private_name=f'{name}'
定义获取(self、obj、objtype=None):
返回getattr(对象,self.private\u名称)
定义设置(自身、对象、值):
自我验证(值)
setattr(对象,self.private_名称,值)
刘道然表示:

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

类验证程序:
定义设置名称(自身、所有者、名称):
self.name=name
定义获取(self、obj、objtype=None):
返回对象名称
定义设置(自身、对象、值):
自我验证(值)
对象名称=值
但是马修·伊根说:

从weakref导入WeakKeyDictionary
类验证程序:
定义初始化(自):
self.data=WeakKeyDictionary()
定义获取(自我、目标、所有者):
返回自我数据[obj]
定义设置(自身、对象、值):
自我验证(值)
自身数据[obj]=值

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

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

不知道为什么第二位作者说你不能那样使用
getattr
。是的,
getattr
调用描述符协议,但是描述符被分配到
类型(obj)。\uuu dict\uuuu[name]
但是您将
private\u name
设置为
f'{name}
,因此不会有任何无限递归。。。如果您在
\uuuu set\u name\uuuuu
中使用
self.private\u name=name
而不是
self.private\u name=f'{name}
,则会出现这种情况,但前两个选项中的任何一个都不是这样做的

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

尽管如此,第二种解决方案并不是不正确的

至于第三种解决方案,我推测这是一种替代方案,它根本不会污染实例名称空间,保留一个单独的名称空间--
WeakKeyDictionary
。这主意不错,但并不比另外两个更“正确”。注意,它确实假设类哈希是基于标识的,但事实并非如此。您可以在类中实现
\uuuuu hash\uuuuuu
以基于其他内容进行哈希,如果您的类“概念上”是不可变的,例如一些
点(x,y)
类不公开任何变体方法,并且该类基于
x
y
的值进行哈希。因此,这将使这种方法更具限制性,但除此之外,它是一种不干扰实例名称空间的聪明解决方案


我认为第一个是最具python风格的,因为它是惯用的。但是,这三个都是有效的解决方案。

我试图将所有作者的概念应用到同一个例子(来自雷蒙德·赫廷格),并编写了
self.private\u name=f'{name}'
而不是
self.name=name
。我会更改它。我认为它不会污染实例的名称空间,它属于实例。是否有任何路径可以通过点符号从实例手动查找存储在WeakKeyDictionary()中的数据?谢谢。@49,嗯?我说的重点是避免污染实例名称空间,就像使用
\u some\u属性的正常方式一样。注意,它不属于实例,描述符属于类。无论如何,要获得一个描述符,您需要类似于
type(instance)。\uu dict\uuu['Validator']。data
我知道。正如您所指出的,(1)如果没有WeakKeyDictionary,属性将存储在实例的命名空间中,并可通过
obj\u实例访问。(2) 有了WeakKeyDictionary,它们就不是了。然而,由于我的_attrib属于这个实例,我还不明白为什么要在这里使用weakkeydiculation。我知道描述符属于类而不是实例。虚线查找顺序(也在实例上)表示查找
类型(实例)。\uuuu dict\uuuu['my\u attrib']
(如果是数据描述符)。