A fews days ago, there occured an error when calling unbound method with an instance object of another type in Python 2 like the following.
In [1]: class Foo:
...: def bar(self):
...: print(self)
...:
In [2]: Foo.bar
Out[2]: <unbound method Foo.bar>
In [3]: Foo.bar(1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-8a5b47d332a0> in <module>()
----> 1 Foo.bar(1)
TypeError: unbound method bar() must be called with Foo instance as first argument (got int instance instead)
But it's ok in in Python 3 environment.
In [1]: class Foo:
...: def bar(self):
...: print(self)
...:
In [2]: Foo.bar(1)
1
Why this semantic is different between Python 2 and Python3?
Let's look into the CPython implementions.
Instance method call implemention in CPython 2
static PyObject *
instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw)
{
PyObject *self = PyMethod_GET_SELF(func);
PyObject *klass = PyMethod_GET_CLASS(func);
PyObject *result;
func = PyMethod_GET_FUNCTION(func);
if (self == NULL) {
/* Unbound methods must be called with an instance of
the class (or a derived class) as first argument */
int ok;
if (PyTuple_Size(arg) >= 1)
self = PyTuple_GET_ITEM(arg, 0);
if (self == NULL)
ok = 0;
else {
ok = PyObject_IsInstance(self, klass);
if (ok < 0)
return NULL;
}
if (!ok) {
char clsbuf[256];
char instbuf[256];
getclassname(klass, clsbuf, sizeof(clsbuf));
getinstclassname(self, instbuf, sizeof(instbuf));
PyErr_Format(PyExc_TypeError,
"unbound method %s%s must be called with "
"%s instance as first argument "
"(got %s%s instead)",
PyEval_GetFuncName(func),
PyEval_GetFuncDesc(func),
clsbuf,
instbuf,
self == NULL ? "" : " instance");
return NULL;
}
Py_INCREF(arg);
}
else {
Py_ssize_t argcount = PyTuple_Size(arg);
PyObject *newarg = PyTuple_New(argcount + 1);
int i;
if (newarg == NULL)
return NULL;
Py_INCREF(self);
PyTuple_SET_ITEM(newarg, 0, self);
for (i = 0; i < argcount; i++) {
PyObject *v = PyTuple_GET_ITEM(arg, i);
Py_XINCREF(v);
PyTuple_SET_ITEM(newarg, i+1, v);
}
arg = newarg;
}
result = PyObject_Call((PyObject *)func, arg, kw);
Py_DECREF(arg);
return result;
}
As you can see, the interpreter will check whether the first argument is an instance of function's class, so the integer 1 can't pass.
A fews days ago, there occured an error when calling unbound method with an instance object of another type in Python 2 like the following.
But it's ok in in Python 3 environment.
Why this semantic is different between Python 2 and Python3?
Let's look into the CPython implementions.
Instance method call implemention in CPython 2
As you can see, the interpreter will check whether the first argument is an instance of function's class, so the integer
1
can't pass.But the thing differs in CPython 3 implemention.
The Python 3 interpreter simply and roughly call the unbound method with all the arguments.
WTF, it's so different and magical.
Should the duck type be anywhere in Python 3?