What is the method-wrapper type in Python 3? If I define a class like so:
class Foo(object): def __init__(self, val): self.val = val def __eq__(self, other): return self.val == other.val
And then do:
Foo(42).__eq__
I get:
<bound method Foo.__eq__ of <__main__.Foo object at 0x10121d0>>
But if I do (in Python 3 ):
Foo(42).__ne__
I get:
<method-wrapper '__ne__' of Foo object at 0x1073e50>
What is a "method-wrapper" type?
Edit: sorry to be more accurate: class method-wrapper
is the type of __ne__
, as if I do:
>>> type(Foo(42).__ne__) <class 'method-wrapper'>
Whereas the type of __eq__
is:
>>> type(Foo(42).__eq__) <class 'method'>
Furthermore method-wrapper
seems to be the type of any undefined magic method on a class (so __le__
, __repr__
, __str__
etc if not explicitly defined will also have this type).
What I am interested in is how the method-wrapper
class is used by Python. Are all "default implementations" of methods on a class just instances of this type?
What are Wrappers in Python? So, wrappers are the functionality available in Python to wrap a function with another function to extend its behavior. Now, the reason to use wrappers in our code lies in the fact that we can modify a wrapped function without actually changing it. They are also known as decorators.
A wrapper method is an adapter or a façade; it provides an alternative interface for an existing method. You've been asked to write a façade (facade) - to provide a simpler interface for clients that don't need to specify high and low values.
Decoration is a way to specify management code for functions and classes. Decorators themselves take the form of callable objects that process other callable objects.
A slot wrapper is installed in the dict of an extension type to access a special method implemented in C. For example, object. __init__ or Integer. __lt__. Note that slot wrappers are always unbound (there is a bound variant called method-wrapper).
It appears that the type <method-wrapper ..>
is used by CPython for methods implemented in C code. Basically the type doesn't wrap another method. Instead it wraps a C-implemented function as an bound method. In this way <method-wrapper>
is exactly like a <bound-method>
except that it is implemented in C.
In CPython there are two special types related to this.
<slot wrapper>
Which (at least) wraps a C-implemented function. Behaves like an <unbound method>
in CPython 2 (at least sometimes) or a <function>
in CPython 3<method-wrapper>
Which wraps a C-implemented function as an bound method. Instances of this type have an __self__
attribute__ which is used as first argument when it is called.If you have a <slot wrapper>
you bind it to an object with __get__
to get an <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")
You can call __get__
on any function-like object in Python to get an <bound method>
or <method-wrapper>
. Note __get__
on both of these types will simply return self.
The type object
in CPython 3 have C-implementations for both __ne__
and __eq__
, and any of the other comparison operators. Thus object.__ne__
returns a <slot wrapper>
for this operator. Likewise object().__ne__
returns a <method-wrapper>
which can be used to compare the this object.
Since you have not defined __ne__
in your class you get a bound method (as <method-wrapper>
) which is the C-implemented function for instance of object (included derived instances). My bet is that this C function will check if you have defined any __eq__
, call this, and then not the result.
Python 2 behaves significantly different here. Since we have the concept of unbound methods. which require that you call them with the proper first-argument type, both <slot wrapper>
and <method-wrapper>
have an __objclass__
which is the type the first-argument must be an instance of.
More over: Python 2 does not have any implementations of comparisons operators for the object
type. Thus object.__ne__
is not a function to compare objects. Rather, interesting, the type type
which is the metaclass of object
does have an C-implemented __ne__
operator. Thus you get an bound method from object.__ne__
that will try to compare the type object
with any other type (or object).
Thus object().__ne__
will actually fail with an AttributeError
since object
does not define any such method. Given that object() == object()
actually works (giving False), I would guess that CPython 2 have special-cases in the interpreter for comparison of objects. Once more we see that CPython 3 have cleaned up some less-fortunate implementation details of Python 2.
This is because 'unbound methods' don't exist in Python 3.
In Python 3000, the concept of unbound methods has been removed, and the expression "A.spam" returns a plain function object. It turned out that the restriction that the first argument had to be an instance of A was rarely helpful in diagnosing problems, and frequently an obstacle to advanced usages --- some have called it "duck typing self" which seems an appropriate name. (Source)
In Python 2.x, we had bound methods and unbound methods. A bound method was bound to an object, meaning that when it was called, it passed the object instance as the first variable (self
, normally). An unbound method was one where the function was a method, but without a instance it belonged to - it would throw an error if something other than an object instance was passed in to the method.
Now, in 3.x, this has been changed. Instead of bound/unbound methods, when you request a method of an object, it returns the function object, but wrapped in a wrapper function that passes the instance variable in. This way there is no need to make a distinction between bound and unbound methods - bound methods are wrapped, unbound are not.
To clarify the difference:
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
can be thought of as:
def method-wrapper(): A.method(a)
For more on this, check out Guido's blog - the history of Python.
Edit:
So, the reason this all applies is that, here, __ne__()
has not been overridden - it's at a default state, which is an identity check, implemented in C (line 980ish). The wrapper is there to provide the method with the above functionality.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With