面向对象:继承
一:什么面向对象的继承?
比较官方的说法就是:
继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别a“继承自”另一个类别b,就把这个a称为“b的子类别”,而把b称为“a的父类别”也可以称“b是a的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
字面意思就是:子承父业,合法继承家产,就是如果你是独生子,而且你也很孝顺,不出意外,你会继承你父母所有家产,他们的所有财产都会由你使用(败家子儿除外)。
那么用一个例子来看一下继承:
class person: def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex class cat: def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex class dog: def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex # 继承的用法: class aniaml(object): def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex class person(aniaml): pass class cat(aniaml): pass class dog(aniaml): pass
view code
继承的有点也是显而易见的:
1,增加了类的耦合性(耦合性不宜多,宜精)。
2,减少了重复代码。
3,使得代码更加规范化,合理化。
二 继承的分类。
就向上面的例子:
aminal 叫做父类,基类,超类。
person cat dog: 子类,派生类。
继承:可以分单继承,多继承。
这里需要补充一下python中类的种类(继承需要):
在python2x版本中存在两种类.:
⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object
三,单继承。
3.1 类名,对象执行父类方法
class aniaml(object): type_name = '动物类' def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex def eat(self): print(self) print('吃东西') class person(aniaml): pass class cat(aniaml): pass class dog(aniaml): pass # 类名: print(person.type_name) # 可以调用父类的属性,方法。 person.eat(111) print(person.type_name) # 对象: # 实例化对象 p1 = person('春哥','男',18) print(p1.__dict__) # 对象执行类的父类的属性,方法。 print(p1.type_name) p1.type_name = '666' print(p1) p1.eat()
类名,对象分别调用父类方法
3.2 执行顺序
class aniaml(object): type_name = '动物类' def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex def eat(self): print(self) print('吃东西') class person(aniaml): def eat(self): print('%s 吃饭'%self.name) class cat(aniaml): pass class dog(aniaml): pass p1 = person('barry','男',18) # 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。 p1.eat() # 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。
执行顺序
3.3同时执行类以及父类方法
方法一:
如果想执行父类的func方法,这个方法并且子类中夜用,那么就在子类的方法中写上:
父类.func(对象,其他参数)
举例说明:
class aniaml(object): type_name = '动物类' def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex def eat(self): print('吃东西') class person(aniaml): def __init__(self,name,sex,age,mind): ''' self = p1 name = '春哥' sex = 'laddboy' age = 18 mind = '有思想' ''' # aniaml.__init__(self,name,sex,age) # 方法一 self.mind = mind def eat(self): super().eat() print('%s 吃饭'%self.name) class cat(aniaml): pass class dog(aniaml): pass # 方法一: aniaml.__init__(self,name,sex,age) # p1 = person('春哥','laddboy',18,'有思想') # print(p1.__dict__) # 对于方法一如果不理解: # def func(self): # print(self) # self = 3 # func(self)
方法二:
利用super,super().func(参数)
class aniaml(object): type_name = '动物类' def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex def eat(self): print('吃东西') class person(aniaml): def __init__(self,name,sex,age,mind): ''' self = p1 name = '春哥' sex = 'laddboy' age = 18 mind = '有思想' ''' # super(person,self).__init__(name,sex,age) # 方法二 super().__init__(name,sex,age) # 方法二 self.mind = mind def eat(self): super().eat() print('%s 吃饭'%self.name) class cat(aniaml): pass class dog(aniaml): pass # p1 = person('春哥','laddboy',18,'有思想') # print(p1.__dict__)
单继承练习题:
# 1 class base: def __init__(self, num): self.num = num def func1(self): print(self.num) class foo(base): pass obj = foo(123) obj.func1() # 123 运⾏的是base中的func1 # 2 class base: def __init__(self, num): self.num = num def func1(self): print(self.num) class foo(base): def func1(self): print("foo. func1", self.num) obj = foo(123) obj.func1() # foo. func1 123 运⾏的是foo中的func1 # 3 class base: def __init__(self, num): self.num = num def func1(self): print(self.num) class foo(base): def func1(self): print("foo. func1", self.num) obj = foo(123) obj.func1() # foo. func1 123 运⾏的是foo中的func1 # 4 class base: def __init__(self, num): self.num = num def func1(self): print(self.num) self.func2() def func2(self): print("base.func2") class foo(base): def func2(self): print("foo.func2") obj = foo(123) obj.func1() # 123 foo.func2 func1是base中的 func2是⼦类中的 # 再来 class base: def __init__(self, num): self.num = num def func1(self): print(self.num) self.func2() def func2(self): print(111, self.num) class foo(base): def func2(self): print(222, self.num) lst = [base(1), base(2), foo(3)] for obj in lst: obj.func2() # 111 1 | 111 2 | 222 3 # 再来 class base: def __init__(self, num): self.num = num def func1(self): print(self.num) self.func2() def func2(self): print(111, self.num) class foo(base): def func2(self): print(222, self.num) lst = [base(1), base(2), foo(3)] for obj in lst: obj.func1() # 那笔来吧. 好好算
view code
四,多继承
class shenxian: # 神仙 def fei(self): print("神仙都会⻜") class monkey: # 猴 def chitao(self): print("猴⼦喜欢吃桃⼦") class sunwukong(shenxian, monkey): # 孙悟空是神仙, 同时也是⼀只猴 pass sxz = sunwukong() # 孙悟空 sxz.chitao() # 会吃桃⼦ sxz.fei() # 会⻜
此时, 孙悟空是⼀只猴⼦, 同时也是⼀个神仙. 那孙悟空继承了这两个类. 孙悟空⾃然就可以执⾏这两个类中的⽅法. 多继承⽤起来简单. 也很好理解. 但是多继承中, 存在着这样⼀个问题. 当两个⽗类中出现了重名⽅法的时候. 这时该怎么办呢? 这时就涉及到如何查找⽗类⽅法的这么⼀个问题.即mro(method resolution order) 问题. 在python中这是⼀个很复杂的问题. 因为在不同的python版本中使⽤的是不同的算法来完成mro的.
这里需要补充一下python中类的种类(继承需要):
在python2x版本中存在两种类.:
⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object
4.1经典类的多继承
虽然在python3中已经不存在经典类了. 但是经典类的mro最好还是学⼀学. 这是⼀种树形结构遍历的⼀个最直接的案例. 在python的继承体系中. 我们可以把类与类继承关系化成⼀个树形结构的图. 来, 上代码:
class a: pass class b(a): pass class c(a): pass class d(b, c): pass class e: pass class f(d, e): pass class g(f, d): pass class h: pass class foo(h, g): pass
代码示例
对付这种mro画图就可以:
继承关系图已经有了. 那如何进⾏查找呢? 记住⼀个原则. 在经典类中采⽤的是深度优先,遍历⽅案. 什么是深度优先. 就是⼀条路走到头. 然后再回来. 继续找下⼀个.
图中每个圈都是准备要送鸡蛋的住址. 箭头和⿊线表⽰线路. 那送鸡蛋的顺序告诉你入⼝在最下⾯r. 并且必须从左往右送. 那怎么送呢?
如图. 肯定是按照123456这样的顺序来送. 那这样的顺序就叫深度优先遍历. ⽽如果是142356呢? 这种被称为⼴度优先遍历. 好了. 深度优先就说这么多. 那么上⾯那个图怎么找的呢? mro是什么呢? 很简单. 记住. 从头开始. 从左往右. ⼀条路跑到头, 然后回头. 继续⼀条路跑到头. 就是经典类的mro算法.
类的mro: foo-> h -> g -> f -> e -> d -> b -> a -> c. 你猜对了么?
4.2新式类的多继承
4.2.1 mro序列
mro是一个有序列表l,在类被创建时就计算出来。
通用计算公式为:
mro(child(base1,base2)) = [ child ] + merge( mro(base1), mro(base2), [ base1, base2] ) (其中child继承自base1, base2)
如果继承至一个基类:class b(a)
这时b的mro序列为
mro( b ) = mro( b(a) ) = [b] + merge( mro(a) + [a] ) = [b] + merge( [a] + [a] ) = [b,a]
如果继承至多个基类:class b(a1, a2, a3 …)
这时b的mro序列
mro(b) = mro( b(a1, a2, a3 …) ) = [b] + merge( mro(a1), mro(a2), mro(a3) ..., [a1, a2, a3] ) = ...
计算结果为列表,列表中至少有一个元素即类自己,如上述示例[a1,a2,a3]。merge操作是c3算法的核心。
4.2.2. 表头和表尾
表头:
列表的第一个元素
表尾:
列表中表头以外的元素集合(可以为空)
示例
列表:[a, b, c]
表头是a,表尾是b和c
4.2.3. 列表之间的+操作
+操作:
[a] + [b] = [a, b]
(以下的计算中默认省略)
---------------------
merge操作示例:
如计算merge( [e,o], [c,e,f,o], [c] ) 有三个列表 : ① ② ③ 1 merge不为空,取出第一个列表列表①的表头e,进行判断 各个列表的表尾分别是[o], [e,f,o],e在这些表尾的集合中,因而跳过当前当前列表 2 取出列表②的表头c,进行判断 c不在各个列表的集合中,因而将c拿出到merge外,并从所有表头删除 merge( [e,o], [c,e,f,o], [c]) = [c] + merge( [e,o], [e,f,o] ) 3 进行下一次新的merge操作 ...... ---------------------
计算mro(a)方式:
mro(a) = mro( a(b,c) ) 原式= [a] + merge( mro(b),mro(c),[b,c] ) mro(b) = mro( b(d,e) ) = [b] + merge( mro(d), mro(e), [d,e] ) # 多继承 = [b] + merge( [d,o] , [e,o] , [d,e] ) # 单继承mro(d(o))=[d,o] = [b,d] + merge( [o] , [e,o] , [e] ) # 拿出并删除d = [b,d,e] + merge([o] , [o]) = [b,d,e,o] mro(c) = mro( c(e,f) ) = [c] + merge( mro(e), mro(f), [e,f] ) = [c] + merge( [e,o] , [f,o] , [e,f] ) = [c,e] + merge( [o] , [f,o] , [f] ) # 跳过o,拿出并删除 = [c,e,f] + merge([o] , [o]) = [c,e,f,o] 原式= [a] + merge( [b,d,e,o], [c,e,f,o], [b,c]) = [a,b] + merge( [d,e,o], [c,e,f,o], [c]) = [a,b,d] + merge( [e,o], [c,e,f,o], [c]) # 跳过e = [a,b,d,c] + merge([e,o], [e,f,o]) = [a,b,d,c,e] + merge([o], [f,o]) # 跳过o = [a,b,d,c,e,f] + merge([o], [o]) = [a,b,d,c,e,f,o] ---------------------
结果ok. 那既然python提供了. 为什么我们还要如此⿇烦的计算mro呢? 因为笔
试.......你在笔试的时候, 是没有电脑的. 所以这个算法要知道. 并且简单的计算要会. 真是项⽬
开发的时候很少有⼈这么去写代码.
这个说完了. 那c3到底怎么看更容易呢? 其实很简单. c3是把我们多个类产⽣的共同继
承留到最后去找. 所以. 我们也可以从图上来看到相关的规律. 这个要⼤家⾃⼰多写多画图就
能感觉到了. 但是如果没有所谓的共同继承关系. 那⼏乎就当成是深度遍历就可以了
下一篇: Python04之数据类型