Python 忽略手动调用描述符

Python manual call to descriptor ignored

class B(object):
    def meth(self):
        pass

class C(B):
    pass

b = B()
c = C()

print c.meth.__get__(b, B)

给我:

<bound method C.meth of <__main__.C object at 0x10a892910>>

但我希望是这样的:

<bound method B.meth of <__main__.B object at 0x?????????>>

为什么我手动调用 c.meth.__get__ 会被忽略? Python 的行为就好像我写了 print c.meth,并且该查找会自动转换为 print c.meth.__get__(c, C)

当您查找 c.meth 时得到的对象是一个描述符,但可能不是您期望的描述符。相反,它是描述符调用的 result(恰好是另一种描述符)。具体来说,c.meth 是一个 instancemethod 对象。这是当函数(例如 B.__dict__["meth"])作为描述符(例如 B.__dict__["meth"].__get__(c, C) 被调用时得到的,我怀疑这是您实际上想自己尝试的描述符调用)。

instancemethod 对象用作描述符时的行为有点奇怪。如果传递给 __get__ 的 class 是方法原始 class 的子 class,未绑定方法将绑定,否则未绑定方法将按原样返回。已经绑定的方法永远不会反弹。

您正在尝试绑定一个已绑定的方法,但该方法不起作用。具体来说,c.meth.__get__(b, B) returns 与 c.meth 相同的 instancemethod 对象。额外的 __get__ 调用被故意视为空操作。

您可以在cpython源代码here中看到__get__方法对绑定方法的实现。不是很长,所以我会复制它:

static PyObject *
instancemethod_descr_get(PyObject *meth, PyObject *obj, PyObject *cls)
{
    /* Don't rebind an already bound method, or an unbound method
       of a class that's not a base class of cls. */

    if (PyMethod_GET_SELF(meth) != NULL) {
        /* Already bound */
        Py_INCREF(meth);
        return meth;
    }
    /* No, it is an unbound method */
    if (PyMethod_GET_CLASS(meth) != NULL && cls != NULL) {
        /* Do subclass test.  If it fails, return meth unchanged. */
        int ok = PyObject_IsSubclass(cls, PyMethod_GET_CLASS(meth));
        if (ok < 0)
            return NULL;
        if (!ok) {
            Py_INCREF(meth);
            return meth;
        }
    }
    /* Bind it to obj */
    return PyMethod_New(PyMethod_GET_FUNCTION(meth), obj, cls);
}