Python 里的多重继承

转自:http://christophor.blog.163.com/blog/static/16215437320107276239434/

Python 里的多重继承

Python支持多重继承,通过查看子类的类对象的__bases__就可以看到它继承的类元组。这里只是针对在多重继承下,子类实例调用子类的方法的原则进行介绍。我们以例子来说明一下:

class A():

    _a = 1

    def Afun(self):

        print 'A'

    def Common(self):

        print 'A-common'

        

class A1():

    

    def Common(self):

        print 'A1-common'

    

        

class B(A,A1):

    _b = 2

    def Bfun(self):

        print 'B'

b = B()

b.Common()

以上的代码输出的是A-common,可以看到python在处理多个基类时,是按继承的顺序来查找相应的属性(成员和方法等)

再看下面的代码,使A继承于AA,并且把方法移到基类AA上去:

class AA():

    def Common(self):

        print 'A-common'

class A( AA ):

    _a = 1

    def Afun(self):

        print 'A'

        

class A1():

    

    def Common(self):

        print 'A1-common'

    

        

class B(A,A1):

    _b = 2

    def Bfun(self):

        print 'B'

这样的输出还是:A-common,可以看到它是深度优先的,就是先找A是否具有Common方法,如果没有则从它的基类AA里寻找,如果都没有再到A1里找。

以上的结果对python的新类同样适用。

以上都比较简单,我们来看下在C++里令人烦躁的菱形继承在python里的表现如何。

class A():

    _a = 1

    def Afun(self):

        print 'A'

    def Common(self):

        print 'A-common'

        

class B(A):

    _b = 2

    def Bfun(self):

        print 'B'

    def Common(self):

        print 'B-common'

class C(A):

    _c = 3

    def CFun(self):

        print 'C'

    def Common(self):

        print 'C-common'

    

class D(C,B):

    _d = 4

    def DFun(self):

        print 'D'    

              

d = D()

d.Common()

 

以上的代码输出:C-common

这也符合我们上面说的。

情况1,把C的Common方法注释掉

class C(A):

    _c = 3

    def CFun(self):

        print 'C'

    '''

    def Common(self):

        print 'C-common'

'''

这时输出的是:A-common

也符合我们的深度优先。

情况2,不改变C,我们改动A,把它变成新类形式(也就是使它继承于object)

class A(object):

    _a = 1

    def Afun(self):

        print 'A'

    def Common(self):

        print 'A-common'

这是输出的结果是:B-common

这个就好像不符合我们刚才所说的规则了,这是为什么呢?

通过跟踪代码,终于找到原因了:其实是在执行d.Common()的时候,执行PyObject_GetAttr时调用tp->tp_getattro,而新类和旧类对应的原类的实现是不一样的:

旧的类(就是A没有继承object)tp->tp_getattro 是

instance_getattr(PyInstanceObject * inst, _object * name) 

核心代码:深度查找

static PyObject *

class_lookup(PyClassObject *cp, PyObject *name, PyClassObject **pclass)

{

Py_ssize_t i, n;

PyObject *value = PyDict_GetItem(cp->cl_dict, name);

if (value != NULL) {

*pclass = cp;

return value;

}

n = PyTuple_Size(cp->cl_bases);

for (i = 0; i < n; i++) {

/* XXX What if one of the bases is not a class? */

PyObject *v = class_lookup(

(PyClassObject *)

PyTuple_GetItem(cp->cl_bases, i), name, pclass);

if (v != NULL)

return v;

}

return NULL;

}

新的类则是tp->tp_getattro 是

PyObject_GenericGetAttr(_object * obj, _object * name)

核心代码:顺序查找

mro = tp->tp_mro;

assert(mro != NULL);

assert(PyTuple_Check(mro));

n = PyTuple_GET_SIZE(mro);

for (i = 0; i < n; i++) {

base = PyTuple_GET_ITEM(mro, i);

if (PyClass_Check(base))

dict = ((PyClassObject *)base)->cl_dict;

else {

assert(PyType_Check(base));

dict = ((PyTypeObject *)base)->tp_dict;

}

assert(dict && PyDict_Check(dict));

descr = PyDict_GetItem(dict, name);

if (descr != NULL)

break;

}

还有一个疑问就是tp->tp_mro是什么时候?怎么设置的?

通过跟踪代码发现tp->tp_mro设置是在生成类的时候设定的,通过

mro_implementation(PyTypeObject *type),而这个函数会过滤掉重复的。

所以新类形式的查找顺序是D -> C -> B -> A -> object,也就是tp->tp_mro元组的顺序。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值