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__
等等,如果没有明确定义也会有这个类型)。
我感兴趣的是Python如何使用method-wrapper
类。类的方法的所有“默认实现”都只是这种类型的实例吗?
答案 0 :(得分:23)
这是因为Python 3中不存在“未绑定的方法”。
在Python 3000中,已删除未绑定方法的概念,表达式“A.spam”返回普通函数对象。事实证明,第一个参数必须是A的一个实例的限制在诊断问题时很少有用,并且经常成为高级用法的障碍 - 有些人称之为“鸭子打字自我”,这似乎是一个恰当的名称。 (Source)
在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的博客 - the history of Python。
编辑:
所以,这一切都适用的原因是,在这里,__ne__()
尚未被覆盖 - 它处于默认状态,即身份检查implemented in C(第980行)。包装器用于为方法提供上述功能。
答案 1 :(得分:18)
CPython似乎使用类型<method-wrapper ..>
来实现在C代码中实现的方法。基本上该类型不包含另一种方法。相反,它将C实现的函数包装为绑定方法。这样<method-wrapper>
就像<bound-method>
一样,只不过它是用C实现的。
在CPython中有两种与此相关的特殊类型。
<slot wrapper>
其中(至少)包装了C实现的函数。在CPython 2中表现得像<unbound method>
(至少有时候)或在CPython 3中表现为<function>
<method-wrapper>
其中包含C实现的函数作为绑定方法。此类型的实例具有__self__
属性___,在调用它时将其用作第一个参数。如果您有<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")
您可以在Python中的任何类似函数的对象上调用__get__
来获取<bound method>
或<method-wrapper>
。注意这两种类型的__get__
只会返回self。
CPython 3中的object
类型具有__ne__
和__eq__
以及任何其他比较运算符的C实现。因此,object.__ne__
会为此运算符返回<slot wrapper>
。同样,object().__ne__
会返回<method-wrapper>
,可用于比较此对象。
由于您没有在类中定义__ne__
,因此您获得了一个绑定方法(如<method-wrapper>
),它是对象(包含派生实例)的C实现函数。我敢打赌,这个C函数将检查你是否定义了任何__eq__
,调用它,然后不结果。
Python 2在这里表现出很大的不同。因为我们有未绑定方法的概念。要求您使用正确的第一个参数类型调用它们,<slot wrapper>
和<method-wrapper>
都有__objclass__
,这是第一个参数必须是实例的类型。
更多内容: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的一些不那么幸运的实现细节。
答案 2 :(得分:0)
下面是我的变量检查例程中的一些测试代码。定义的类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__
默认为基类object
上的method-wrapper
并提供默认输出。
__str__
被定义(因此是方法),因此测试hasattr(t5.__str__, '__code__')
的结果为True。