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);
}
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);
}