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

时间:2016-06-14 19:27:40

标签: 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>)

Searching the docs出现了非常有限但有趣的结果:

  1. A note in the inspect module inspect.getattr_static无法解析描述符并包含可用于解析它们的代码。
  2. an optimization made in python 2.4声称method_descriptorwrapper_descriptor 更高效但不解释它们是什么:
      

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

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

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

    >>> import types
    >>> type(list.append) in vars(types).values()
    False
    >>> type(list.__add__) in vars(types).values()
    False
    

    使用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__
    

    Searching the internet只提出了“什么是描述符”的结果或者涉及特定类型的模糊引用。

    所以我的问题是:

    <class 'method_descriptor'><class 'wrapper_descriptor'>之间的实际差异是什么?

2 个答案:

答案 0 :(得分:2)

这是一个实施细节。在C级别,像list这样的内置类型通过append结构数组按名称定义PyMethodDef等方法,而__add__等特殊方法更间接定义。

__add__对应于类型&#39; s sq_concattp_as_sequence类型nb_add中的两个广告位tp_as_number中的任一个中的函数指针{1}}。如果某个类型定义了其中一个插槽,那么Python会为Python级API的wrapper_descriptor方法生成一个__add__包装槽。

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

如果您想查看代码,method_descriptorwrapper_descriptor都在Objects/descrobject.c中实现,Include/descrobject.h中包含struct typedefs。您可以在Objects/typeobject.c中看到初始化包装器的代码,其中PyType_Ready委托给add_operators的{​​{1}}和wrapper_descriptor的{​​{1}}。

答案 1 :(得分:0)

似乎 Dim ListView1 As ListView ListView1 = New ListView() method_descriptor是CPython中可用的一种callable。 它们之间的区别似乎很简单 wrapper_descriptor显然用于内置方法 (在C中实现)对象:

method_descriptor

set.__dict__['union'].__class__ <class 'wrapper_descriptor'> 用于内置类型的运算符:

wrapper_descriptor

This is the place我找到了这些信息。