"包装器"和"方法"描述符之间的区别?

Tad*_*sen 9 python

我正在编写一个使用内省找到类的"未绑定方法"的代码,并且惊讶地发现内置类型有两种不同的描述符:

>>> type(list.append), list.append
(<class 'method_descriptor'>, <method 'append' of 'list' objects>)
>>> type(list.__add__), list.__add__
(<class 'wrapper_descriptor'>, <slot wrapper '__add__' of 'list' objects>)
Run Code Online (Sandbox Code Playgroud)

搜索文档非常有限但有趣的结果:

  1. inspect模块中的注释,inspect.getattr_static它不解析描述符并包含可用于解析它们的代码.
  2. 在Python 2.4中做了优化,声称method_descriptor更有效的wrapper_descriptor,但是不解释它们是什么:

    的方法list.__getitem__(),dict.__getitem__()dict.__contains__()现在被实现为method_descriptor对象,而不是wrapper_descriptor对象.这种访问形式使其性能翻倍,并使它们更适合用作功能的参数:map(mydict.__getitem__, keylist).

性能上的差异引起了我的兴趣,显然存在差异所以我去寻找其他信息.

这些类型都不在模块中types:

>>> import types
>>> type(list.append) in vars(types).values()
False
>>> type(list.__add__) in vars(types).values()
False
Run Code Online (Sandbox Code Playgroud)

使用help不提供任何有用的信息:

>>> help(type(list.append))
Help on class method_descriptor in module builtins:

class method_descriptor(object)
 |  Methods defined here:
 |  
    <generic descriptions for>
      __call__, __get__, __getattribute__, __reduce__, and __repr__
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __objclass__
 |  
 |  __text_signature__

>>> help(type(list.__add__))
Help on class wrapper_descriptor in module builtins:

class wrapper_descriptor(object)
 |  Methods defined here:
 |  
    <generic descriptions for>
      __call__, __get__, __getattribute__, __reduce__, and __repr__
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __objclass__
 |  
 |  __text_signature__
Run Code Online (Sandbox Code Playgroud)

在互联网上搜索只得到关于"什么是描述符"的结果或对所涉及的特定类型的模糊引用.

所以我的问题是:

<class 'method_descriptor'>和之间的实际区别是<class 'wrapper_descriptor'>什么?

use*_*ica 4

这是一个实施细节。在 C 级别,内置类型通过结构数组按名称list定义方法,而特殊方法则更间接地定义。appendPyMethodDef__add__

__add__sq_concat对应于类型的tp_as_sequencenb_add类型的两个槽中的任意一个中的函数指针tp_as_number。如果某个类型定义了其中一个槽,Python 会wrapper_descriptor__add__Python 级 API 的方法生成该槽的包装。

类型槽和结构所需的包装PyMethodDef有点不同;例如,两个槽可以对应一种方法,或者一个槽可以对应六种方法。插槽也不带有方法名称,而方法名称是PyMethodDef. 由于这两种情况需要不同的代码,Python 使用不同的包装器类型来包装它们。

如果您想查看代码, 和 都method_descriptorwrapper_descriptor中实现Objects/descrobject.c,其中 struct typedef 在 中Include/descrobject.h。您可以在 中看到初始化包装器的代码Objects/typeobject.c,其中PyType_Ready委托给add_operatorsfor wrapper_descriptors 和add_methodsfor method_descriptors。