代码之家  ›  专栏  ›  技术社区  ›  wim

MonkeyPatching函数命名空间

  •  4
  • wim  · 技术社区  · 6 年前

    灵感来自 "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 不能?

    2 回复  |  直到 6 年前
        1
  •  5
  •   ShadowRanger    6 年前

    简短的版本:因为cpython PyDict_GetItem 函数不是子类友好的(它不是有意的,更清楚地说; PyDict_* 功能都是专门用于 PyDict_Object 本身,而不是广义映射)。

    长版本:检索属性调用 PyObject_GetAttr . 对于没有显式定义自定义的类 tp_getattro tp_getattr (和) PyFunction_Type does not )结果是 calling PyObject_GenericGetAttr . 假设在类本身上找不到任何内容, PyObject_GenericGetAttr (好吧,实现它的私有API函数) retrieves __dict__ as normal, then calls PyDict_GetItem to retrieve the value . Pydict获取项目 明确使用 dict 执行访问的C级内部构件,绕过任何自定义 __getitem__ 你可能已经定义了。所以你的习惯 _获取项目__ 从未被调用;出于所有实际目的, 双关语 子类只是 双关语 .

    我原本希望你 可以 能够通过 the officially supported __missing__ hook 但事实证明,只有当 _获取项目__ ( dict_subscript )不是通过C级直接访问API调用的,例如 Pydict获取项目 (不经过 听写下标 一点也不)。

    基本上,CPython似乎已经做出了一个选择,将性能优先于完全的灵活性。任何 双关语 子类用作 _口述__ 将被访问,就像它是一个普通的 双关语 (如果子类在假装存储另一个值的同时使用某种魔法来存储一个值,这可能会有点混乱,因为魔法被绕过了),并且所有的映射都不是 双关语 子类在分配时被拒绝(您得到 TypeError 当你试图把它们分配给 f.__dict__ )

        2
  •  -2
  •   Paul Cornelius    6 年前

    文档中的第一句话 __getitem__ 阅读:“调用以实现对self[key]的评估。”您假设(希望)对 f.k1 执行显式操作 f.__dict__["k1"] 但是没有。如果在 _获取项目__ 您将看到这个函数永远不会被您的代码调用。

    我不确定这是为什么,但我怀疑这是出于性能原因。无论如何,我想不出一个好的用例来替换函数的 __dict__ . 有太多的方法会失败。