面向对象之继承封装与多态
Posted chenjienian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象之继承封装与多态相关的知识,希望对你有一定的参考价值。
继承
继承是面向对象中的一个重要概念,通过如果要创建的类与以有的类大部分属性、方法类似,那么可以通过继承的方式创建。一个类可以继承一个或多个类,继承一个类称为单继承,继承多个类称为多继承。被继承的类称为父类,也称为超类或基类,继承的类称为子类或派生类。
继承的实现
class Animal: def __init__(self, name, age, color): self.name = name self.age = age self.color = color def eat(self): print("%s is eating" % self.name) def play(self): print("%s is playing" % self.name) class Cat(Animal): pass class Dog(Animal): pass class Husky(Dog, Animal): # 多继承 pass
查看继承
# __base__查看从左到右继承的第一个类;__bases__是查看继承的所有父类 print(Cat.__base__) # <class ‘__main__.Animal‘> print(Husky.__base__) # <class ‘__main__.Dog‘> print(Husky.__bases__) # (<class ‘__main__.Dog‘>, <class ‘__main__.Animal‘>)
在python3 中,如果怕不指定基类,则会默认继承object类,object类是所有python类的基类。继承object类的类称为新式类,否则称为经典类,python3中的类都是新式类,Python2既有新式类,又有经典类。
继承的好处
继承的一个很重要的好处就是能够减少重复的代码
1 class Animal: 2 def __init__(self, name, age, color): 3 self.name = name 4 self.age = age 5 self.color = color 6 7 def eat(self): 8 print("%s is eating" % self.name) 9 10 def play(self): 11 print("%s is playing" % self.name) 12 13 14 class Cat(Animal): # 继承Animal类 15 def climb(self): 16 print("climb a tree") 17 18 def catch_mouses(self): 19 print("catch a mouse") 20 21 22 class Dog(Animal): 23 def eat(self): 24 print("eat bones") 25 26 def guard(self): 27 print("guard the door") 28 29 30 class Husky(Dog, Animal): # 多继承,同时继承Dog类和Animal类 31 def bite_shoes(self): 32 print("bite shoes") 33 34 35 c1 = Cat("pikaqiu", 1, "orange") 36 d1 = Dog("sara", 2, "white") 37 h1 = Husky("kevin", 1, "grey") 38 39 c1.eat() # 调用Animal中的方法 pikaqiu is eating 40 c1.climb() # 调用Cat类的方法 climb a tree 41 42 d1.eat() # 先从Dog类里面寻找,找到了就不去animal类里找 eat bones 43 d1.play() # 调用Animal类的方法 sara is playing 44 45 h1.eat() # 调用Dog类中的eat方法 eat bones 46 h1.guard() # 调用Dog类中的guard方法 guard the door 47 h1.play() # 调用Animal类的play方法 kevin is playing 48 h1.bite_shoes() # 调用Husky类的方法 bite shoes
多继承的查找顺序
经典类:深度优先
class A: pass class B(A): pass class C(A): pass class D(C, A): pass class E(D, B): pass class F(E, D): pass
将上述的继承顺序以图的形式画出来,红色为继承顺序,绿色为查找顺序
查找顺序为: F-->E-->D-->C-->A
新式类:C3算法
(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算法的核心。
(2) 表头与表尾
表头:列表的第一个元素
表尾:列表中除表头以外的元素的集合(可以为空)
示例:列表 [A, B, C]
表头是A,表尾是B和C
(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外,并将所有表头的C删除 merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] ) 3 进行下一次新的merge操作 ...... ---------------------
示例,有如下继承关系,要计算其MRO顺序
class O(object): pass class D(O): pass class E(O): pass class F(O): pass class B(D, E): pass class C(E, F): pass class A(B, C): pass
首先画出其继承关系图
然后计算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] ---------------------
在python里面可以用A.__mro__的方法查看mro列表
print(A.__mro__) # (<class ‘__main__.A‘>, <class ‘__main__.B‘>, <class ‘__main__.D‘>, <class ‘__main__.C‘>, <class ‘__main__.E‘>, <class ‘__main__.F‘>, <class ‘__main__.O‘>, <class ‘object‘>)
下面再来练习以下,写出如下的MRO列表
class O(object): pass class H(O): pass class I(object): pass class M(object): pass class D(O, M): pass class N(M): pass class E(H, I): pass class B(D, E): pass class G(object): pass class F(G): pass class C(E, F): pass class A(B, C): pass
首先画出继承关系图
按照上面的merge操作计算:
mro(A)=mro(A(B,C)) = [A] + merge(mro(B),mro(C),[B,C]) 先算mro(B) mro(B) = mro(B(D,E)) = [B] + merge(mro(D),mro(E),[D,E]) = [B] + merge([D,M,N,O],[E,H,O,I],[D,E]) = [B] + [D] + merge([M,N,O],[E,H,O,I],[E]) = [B,D] + [M] + merge([N,O],[E,H,O,I],[E]) = [B,D,M] + [N] + merge([O],[E,H,O,I],[E]) = [B,D,M,N] + [E] + merge([O],[H,O,I]) = [B,D,M,N,E] + [H] + [O,I] = [B,D,M,N,E,H,O,I] 再算mro(C) mro(C) = mro(C(E,F)) = [C] + merge(mro(E),mro(F),[E,F]) = [C] + merge([E,H,O,I],[F,G],[E,F]) = [C] + [E] + merge([H,O,I],[F,G],[F]) = [C,E] + [H] + merge([O,I],[F,G],[F]) = [C,E,H,O,I] + merge([F,G],[F]) = [C,E,H,O,I,F,G] 将mro(B)和mro(C)代入mro(A)里面 mro(A) = [A] + merge([B,D,M,N,E,H,O,I],[C,E,H,O,I,F,G],[B,C]) = [A] + [B] + merge([D,M,N,E,H,O,I],[C,E,H,O,I,F,G],[C]) = [A,B] + [D] + merge([M,N,E,H,O,I],[C,E,H,O,I,F,G],[C]) = [A,B,D,M,N] + merge([E,H,O,I],[C,E,H,O,I,F,G],[C]) = [A,B,D,M,N,C] + merge([E,H,O,I],[E,H,O,I,F,G]) = [A,B,D,M,N,C,E,H,O,I,F,G] 最终的mro(A)=[A,B,D,M,N,C,E,H,O,I,F,G]
验证结果
print(A.__mro__) # (<class ‘__main__.A‘>, <class ‘__main__.B‘>, <class ‘__main__.D‘>, <class ‘__main__.M‘>, <class ‘__main__.N‘>, <class ‘__main__.C‘>, <class ‘__main__.E‘>, <class ‘__main__.H‘>, <class ‘__main__.O‘>, <class ‘__main__.I‘>, <class ‘__main__.F‘>, <class ‘__main__.G‘>, <class ‘object‘>)
可以发现,C3是把我们多个类产?的共同继承留到最后去找 ,如果没有所谓的共同继承关系. ?乎就可以当成是深度遍历。
封装
封装,顾名思义是将内容封装到某个地方,然后再去调用被封装到某处的内容。所以运用面向对象的封装特性时,需要注意两点:
将内容封装到某处
从某处调用被封装的内容
1. 将内容封装到某处
class Person: def __init__(self, name, age): self.name = name # 这里就是封装! self.age = age # 把name,age这两个变量封装到对象的name和age这个属性里
2. 从某处调用被封装的内容
调用被封装的内容有两种方式:直接调用和间接调用
class Person: def __init__(self, name, age): self.name = name # 这里就是封装! self.age = age # 把name,age这两个变量封装到对象的name和age这个属性里 def get_name(self): print(self.name) # 间接调用 p1 = Person("章北海", 25) print(p1.name) # 直接调用 p1.get_name() # 间接调用
小结:面向对象中封装就是将内容(属性或方法)封装到对象中,然后通过对象直接或间接获取到被封装的内容
多态
多态,就是一个对象多种状态,python是默认支持多态的
# 在java或者c#定义变量或者给函数传值必须定义数据类型,否则就报错。 def func(int a): print(‘a必须是数字‘) # 而类似于python这种弱定义类语言,a可以是任意形态(str,int,object等等)。 def func(a): print(‘a是什么都可以‘) # 再比如: class F1: pass class S1(F1): def show(self): print ‘S1.show‘ class S2(F1): def show(self): print ‘S2.show‘ # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj): """Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show Python伪代码实现Java或C # 的多态 多态举例
python通过鸭子模型实现多态
python中有一句谚语说的好,你看起来像鸭子,那么你就是鸭子。 对于代码上的解释其实很简答: class A: def f1(self): print(‘in A f1‘) def f2(self): print(‘in A f2‘) class B: def f1(self): print(‘in A f1‘) def f2(self): print(‘in A f2‘) obj = A() obj.f1() obj.f2() obj2 = B() obj2.f1() obj2.f2() # A 和 B两个类完全没有耦合性,但是在某种意义上他们却统一了一个标准。 # 对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互称为鸭子类型。 # 这样的例子比比皆是:str tuple list 都有 index方法,这就是统一了规范。 # str bytes 等等 这就是互称为鸭子类型。 鸭子类型
以上是关于面向对象之继承封装与多态的主要内容,如果未能解决你的问题,请参考以下文章
Python3-2020-测试开发-20- 面向对象之封装,继承,多态