Python method-wrapper type?
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.
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.
Python 3
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 (not asked but answered)
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.