【问题标题】:Python manual call to descriptor ignoredPython手动调用描述符被忽略
【发布时间】:2016-07-30 08:12:10
【问题描述】:
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 是一个绑定的方法对象。坦率地说,我很惊讶它有自己的 __get__ 方法。没有理由期望它的工作方式与原始函数对象的描述符相同。

标签: python python-2.7 inheritance methods descriptor


【解决方案1】:

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

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

您正在尝试绑定已绑定的方法,但该方法不起作用。具体来说,c.meth.__get__(b, B) 返回与 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 是,则未绑定的方法将绑定方法的原始类的子类,..."?
  • 嗯,我想这比我想说的更准确。我在想“如果对象是一个子类的实例”(但我省略了“实例”位),但查看代码它实际上检查的是cls参数而不是@987654336 @。我会编辑得更清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-22
  • 2019-10-28
  • 2022-06-10
  • 2011-05-15
  • 1970-01-01
  • 2012-11-06
  • 1970-01-01
相关资源
最近更新 更多