面向对象之继承封装与多态

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 等等 这就是互称为鸭子类型。

鸭子类型




以上是关于面向对象之继承封装与多态的主要内容,如果未能解决你的问题,请参考以下文章

面向对象之:三大特性:继承(已讲),封装,多态

19.Python面向对象之:三大特性:继承,封装,多态。

Python3-2020-测试开发-20- 面向对象之封装,继承,多态

Python进阶(十六)----面向对象之~封装,多态,鸭子模型,super原理(单继承原理,多继承原理)

面向对象之封装继承多态

JAVA面向对象,继承、封装、多态