受"Define @property on function"的启发,我试图重写函数的命名空间:
>>> class MyDict(dict):
... def __getitem__(self, item):
... if item == 'k2':
... return 'v2'
... return super().__getitem__(item)
...
>>> def f():
... pass
...
...
>>> f.__dict__ = MyDict({'k1': 'v1'})
>>>
>>> f.k1
'v1'
>>> f.k2
AttributeError: 'function' object has no attribute 'k2'为什么f.k1可以解决问题,而f.k2不能解决呢?
发布于 2018-05-23 00:56:00
简短的版本:因为CPython的PyDict_GetItem函数不是子类友好的(这并不是要明确的;PyDict_*函数都是专门针对PyDict_Object本身的,而不是通用映射)。
长版本:检索属性将调用PyObject_GetAttr。对于没有显式定义自定义tp_getattro或tp_getattr (和 does not)的类,这将导致PyObject_GenericGetAttr。假设在类本身上没有发现任何东西,PyObject_GenericGetAttr (嗯,实现它的私有API函数) to retrieve the value。PyDict_GetItem显式地使用dict的C级内部来执行访问,绕过您可能定义的任何自定义__getitem__,因此您的自定义__getitem__永远不会被调用;从所有实际目的来看,dict子类只是一个dict。
我最初希望您能够通过 hook实现这个特定案例的工作,但事实证明,只有在调用了等效的__getitem__ (dict_subscript)时,才会调用这种情况,而不是通过C级的直接访问API(如PyDict_GetItem (根本不通过dict_subscript ))。
基本上,在这里,CPython似乎做出了优先考虑性能而不是完全灵活性的选择。任何用作dict的__dict__子类都会被访问,就好像它是一个普通的dict (如果子类做了一些魔术来存储一个值,而假装存储了一个不同的值,因为魔术被绕过了),那么这个子类可能会变得有点奇怪,并且所有不是dict子类的映射在赋值时都会被拒绝(当您试图将它们分配给f.__dict__时,您会得到一个TypeError )。
发布于 2018-05-23 00:55:20
__getitem__文档中的第一句是:“调用以实现自键的评估”。您假设(希望) f.k1的计算值执行显式操作f.__dict__["k1"],但它不执行。如果您在__getitem__的第一行中放置了一个print语句,您将看到代码从未调用该函数。
我不知道这是为什么,但我怀疑这是出于性能原因。无论如何,我想不出一个很好的用例来替换函数的__dict__。有太多的方法可能会失败。
https://stackoverflow.com/questions/50478162
复制相似问题