Python方法包装器类型?

Ada*_*kin 32 types python-3.x

Python 3中的方法包装器类型是什么?如果我这样定义一个类:

class Foo(object):
    def __init__(self, val):
        self.val = val
    def __eq__(self, other):
        return self.val == other.val
Run Code Online (Sandbox Code Playgroud)

然后做:

Foo(42).__eq__
Run Code Online (Sandbox Code Playgroud)

我明白了:

<bound method Foo.__eq__ of <__main__.Foo object at 0x10121d0>>
Run Code Online (Sandbox Code Playgroud)

但如果我这样做(在Python 3中):

Foo(42).__ne__
Run Code Online (Sandbox Code Playgroud)

我明白了:

<method-wrapper '__ne__' of Foo object at 0x1073e50>
Run Code Online (Sandbox Code Playgroud)

什么是"方法包装"类型?

编辑:抱歉更准确:class method-wrapper是类型__ne__,就像我做的那样:

>>> type(Foo(42).__ne__)
<class 'method-wrapper'>
Run Code Online (Sandbox Code Playgroud)

而类型__eq__是:

>>> type(Foo(42).__eq__)
<class 'method'>
Run Code Online (Sandbox Code Playgroud)

此外,method-wrapper似乎是类上任何未定义的魔术方法的类型(因此__le__,如果没有明确定义__repr__,则__str__等也将具有此类型).

我感兴趣的是method-wrapperPython 如何使用该类.类的方法的所有"默认实现"都只是这种类型的实例吗?

Gar*_*tty 25

这是因为Python 3中不存在"未绑定方法".

在Python 3000中,已删除未绑定方法的概念,表达式"A.spam"返回普通函数对象.事实证明,第一个参数必须是A的一个实例的限制在诊断问题时很少有用,并且经常成为高级用法的障碍 - 有些人称之为"鸭子打字自我",这似乎是一个恰当的名称. (资源)

在Python 2.x中,我们绑定了方法和未绑定的方法.绑定方法绑定到一个对象,这意味着当它被调用时,它将对象实例作为第一个变量(self通常)传递.一个未绑定的方法是函数是一个方法,但没有它所属的实例 - 如果将一个对象实例以外的东西传递给该方法,它将抛出一个错误.

现在,在3.x中,这已经改变了.当您请求对象的方法时,它不是绑定/未绑定的方法,而是返回函数对象,但是包装在传递实例变量的包装函数中.这样就不需要区分绑定和未绑定的方法了. - 绑定方法是包装的,未绑定的方法不包装.

澄清差异:

2.X:

a = A()
f = A.method # f is an unbound method - you must pass an instance of `A` in to it as the first argument.
f = a.method # f is a bound method, bound to the instance `a`.
Run Code Online (Sandbox Code Playgroud)

3.X:

a = A()
f = A.method # f is a function
f = a.method # f is a wrapped function with it's first argument filled with `a`.
Run Code Online (Sandbox Code Playgroud)

a.method 可以被认为是:

def method-wrapper():
    A.method(a)
Run Code Online (Sandbox Code Playgroud)

有关这方面的更多信息,请查看Guido的博客 - Python的历史.

编辑:

所以,这一切都适用的原因是,这里__ne__()没有被覆盖 - 它处于默认状态,这是一个身份检查,用C(第980行)实现.包装器用于为该方法提供上述功能.

  • -1方法包装器也存在于python 2中. (5认同)
  • 对不起,我仍然认为我的问题没有得到解答:什么是`method-wrapper`类型?对于`__eq__`和`__ne__`方法,类型是不同的,但它们都接收隐含的第一个参数.你是说一个`method-wrapper`类型的方法是一个不知道它属于哪个实例的方法?但如果是这种情况,那么`someinst .__ ne __(otherinst)`是如何工作的(因为它绑定到`someinst`实例)? (2认同)

dri*_*iax 23

似乎<method-wrapper ..>CPython使用该类型用于在C代码中实现的方法.基本上该类型不包含另一种方法.相反,它将C实现的函数包装为绑定方法.这种方式<method-wrapper>完全像a,<bound-method> 除了它是用C实现的.

在CPython中有两种与此相关的特殊类型.

  • <slot wrapper>哪个(至少)包装了C实现的函数.<unbound method>在CPython 2(至少有时)或<function>CPython 3中表现得像
  • <method-wrapper>其中将C实现的函数包装为绑定方法.这种类型的实例有一个__self__attribute__,它在被调用时用作第一个参数.

如果你有一个<slot wrapper>绑定到一个对象,__get__得到一个<method-wrapper>:

# returns a <slot_wrapper> on both CPython 2 and 3
sw = object.__getattribute__  

# returns a <method-wrapper>
bound_method = sw.__get__(object()) 

# In this case raises AttributeError since no "some_attribute" exists.
bound_method("some_attribute")  
Run Code Online (Sandbox Code Playgroud)

您可以__get__在Python中调用任何类似函数的对象来获取<bound method><method-wrapper>.__get__关于这两种类型的注意事项只会返回self.

Python 3

类型object中的CPython 3具有C-实现两个__ne____eq__,以及任何其他的比较运算符.因此object.__ne__返回<slot wrapper>此运算符的a.同样object().__ne__返回一个<method-wrapper>可用于比较此对象的a.

由于您尚未__ne__在类中定义,因此您将获得一个绑定方法(as <method-wrapper>),它是对象(包含派生实例)的C实现函数.我敢打赌,这个C函数将检查你是否定义了任何__eq__,调用它,然后不是结果.

Python 2(没问,但回答)

Python 2在这里表现出很大的不同.因为我们有未绑定方法的概念.这要求您使用正确的第一个参数类型调用它们,<slot wrapper>并且<method-wrapper>具有__objclass__哪个是first-argument必须是其实例的类型.

更多内容:Python 2没有任何object类型的比较运算符实现.因此object.__ne__,不是比较对象的函数.相反,有趣的type是,元类的类型object确实有一个C实现的__ne__运算符.因此,您将获得一个绑定方法object.__ne__,尝试将类型object与任何其他类型(或对象)进行比较.

因此object().__ne__实际上将失败,AttributeError因为object没有定义任何这样的方法.鉴于object() == object()实际上有效(给出False),我猜测CPython 2在解释器中有特殊情况用于比较对象.我们再次看到CPython 3已经清理了Python 2的一些不那么幸运的实现细节.

  • Slot-wrapper 和 method-wrapper 对象在 Python 3 上也有 `__objclass__`。另外,slot-wrapper和method-wrapper只用于C槽对应的方法;还有一堆其他的 C 方法类型,最突出的是方法描述符和内置函数或方法,方法描述符用于像 `list.append` 这样的方法,内置函数或方法用于绑定方法像`[].append`(也适用于函数,比如`max`)。 (2认同)