42

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

class Foo(object):
    def __init__(self, val):
        self.val = val
    def __eq__(self, other):
        return self.val == other.val

然后做:

Foo(42).__eq__

我得到:

<bound method Foo.__eq__ of <__main__.Foo object at 0x10121d0>>

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

Foo(42).__ne__

我得到:

<method-wrapper '__ne__' of Foo object at 0x1073e50>

什么是“方法包装器”类型?

编辑:对不起,更准确地说:class method-wrapper是 的类型__ne__,就好像我做的那样:

>>> type(Foo(42).__ne__)
<class 'method-wrapper'>

而类型__eq__是:

>>> type(Foo(42).__eq__)
<class 'method'>

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

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

4

3 回答 3

32

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

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

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

如果您有 a <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")  

您可以__get__在 Python 中调用任何类似函数的对象来获取<bound method>or <method-wrapper>。注意__get__这两种类型都会简单地返回 self。

蟒蛇 3

CPython 3 中的类型对and和任何其他比较运算符object都有 C 实现。因此为此运算符返回 a 。同样返回可用于比较 this 对象的 a。__ne____eq__object.__ne__<slot wrapper>object().__ne__<method-wrapper>

由于您没有__ne__在您的类中定义,您将获得一个绑定方法(as <method-wrapper>),它是对象实例(包括派生实例)的 C 实现函数。我敢打赌,这个 C 函数将检查您是否定义了 any __eq__,调用它,然后检查结果。

Python 2(未问但已回答)

Python 2 在这里的表现明显不同。因为我们有未绑定方法的概念。这要求您使用正确的第一个参数类型来调用它们,并且两者<slot wrapper>都有<method-wrapper>一个__objclass__which 是第一个参数必须是实例的类型。

此外: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 的一些不太幸运的实现细节。

于 2013-10-23T15:26:15.380 回答
31

这是因为 Python 3 中不存在“未绑定方法”。

在 Python 3000 中,未绑定方法的概念已被移除,表达式“A.spam”返回一个普通的函数对象。事实证明,第一个参数必须是 A 的实例的限制对诊断问题几乎没有帮助,并且经常成为高级用法的障碍——有些人称其为“duck typing self”,这似乎是一个合适的名称。 (来源)

在 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`.

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`.

a.method可以认为是:

def method-wrapper():
    A.method(a)

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

编辑:

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

于 2012-05-03T14:56:49.060 回答
-1

下面是我的变量检查例程中的一些测试代码。定义类 Tst,它有一个__str__方法。要确定一个类的实例是否已定义__repr____str__定义,您可以测试hasattr(obj.__str__, '__code__')

例子:

class Tst(object):
    """ Test base class.
    """
    cl_var = 15

    def __init__(self, in_var):
        self.ins_var = in_var

    def __str__(self):
        # Construct to build the classname ({self.__class__.__name__}).
        # so it works for subclass too.
        result = f"{self.__class__.__name__}"
        result += f"(cl_var: {self.__class__.cl_var}, ins_var: {self.ins_var})"
        return result

    def show(self):
        """ Test method
        """
        print(self.ins_var)


t5 = Tst(299)


print('\n\n----- repr() ------')
print(f"obj.repr(): {repr(t5)}")
print(f"obj.repr.class type: {type(t5.__repr__.__class__)}")
print(f"obj.repr.class.name: {t5.__repr__.__class__.__name__}")
print(f"obj.__repr__ has code: {hasattr(t5.__repr__, '__code__')}")

print('\n\n----- str() ------')
print(f"obj.str(): {str(t5)}")
print(f"obj.str.class type: {type(t5.__str__.__class__)}")
print(f"obj.str.class.name: {t5.__str__.__class__.__name__}")
print(f"obj.__str__ has code: {hasattr(t5.__str__, '__code__')}")

回报:

----- repr() ------
obj.repr(): <__main__.Tst object at 0x107716198>
obj.repr.class type: <class 'type'>
obj.repr.class.name: method-wrapper
obj.__repr__ has code: False


----- str() ------
obj.str(): Tst(cl_var: 15, ins_var: 299)
obj.str.class type: <class 'type'>
obj.str.class.name: method
obj.__str__ has code: True

由于__repr__未在类上定义,因此它默认为__repr__on 基类objectmethod-wrapper并提供默认输出。 __str__已定义(因此也是一种方法),因此测试hasattr(t5.__str__, '__code__')结果为 True。

于 2019-04-02T15:48:18.803 回答