universe-proton / universe-topology

A universal computer knowledge topology for all the programmers worldwide.
Apache License 2.0
50 stars 0 forks source link

Why name refers to a function within a class is interpreted as an unbound method in Python 2 rather a general function in Python 3? #10

Open justdoit0823 opened 7 years ago

justdoit0823 commented 7 years ago

Recently, I found a new interesting problem. When I import an another function within class body, the function is interpreted as an instancemethod in Python2 but not as the original. But when I test in Python 3, things seem to work in the intuitive way, and the name refers to the original function. We can easily test this case with IPython.

In Python 2.

In [1]: def foo():
   ...:     print('foo')
   ...:

In [2]: type(foo)
Out[2]: function

In [3]: class A:
   ...:     foo = foo
   ...:

In [4]: A.foo
Out[4]: <unbound method A.foo>

In [5]: type(A.foo)
Out[5]: instancemethod

In [6]: A.foo is foo
Out[6]: False

In Python 3.

In [1]: def foo():
   ...:     print('foo')
   ...:

In [2]: class A:
   ...:     foo = foo
   ...:

In [3]: type(foo)
Out[3]: function

In [4]: A.foo
Out[4]: <function __main__.foo>

In [5]: type(A.foo)
Out[5]: function

In [6]: A.foo is foo
Out[6]: True

We can also test with type. But why the behaviors are different in the two Python versions?

Internally, the function type supports descriptor protocol and can be customized when accessed as a object's attribute. So the magic is hid in the func_descr_get function.

function type's tp_descr_get implementation in Python 2

/* Bind a function to an object */
static PyObject *
func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
{
    if (obj == Py_None)
        obj = NULL;
    return PyMethod_New(func, obj, type);
}

function type's tp_descr_get implementation in Python 3

/* Bind a function to an object */
static PyObject *
func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
{
    if (obj == Py_None || obj == NULL) {
        Py_INCREF(func);
        return func;
    }
    return PyMethod_New(func, obj);
}

So it's obvious that a general function will not be interpreted as an instance method in Python 3. Does the Python 3 work in the right way?

jasonmyers commented 7 years ago

Just for some references, here's the line in the 3.0 changelog (second bullet):

https://docs.python.org/3.0/whatsnew/3.0.html#operators-and-special-methods

And the last bullet here (author is GvR)

http://www.artima.com/weblogs/viewpost.jsp?thread=86641#loose-ends

Edit: and here's the dev list discussion prior to the change https://mail.python.org/pipermail/python-dev/2005-January/050625.html

justdoit0823 commented 7 years ago

@jasonmyers Thanks, the above links are helpful.