zoukankan      html  css  js  c++  java
  • 面向对象之继承、封装与多态

    继承

    继承是面向对象中的一个重要概念,通过如果要创建的类与以有的类大部分属性、方法类似,那么可以通过继承的方式创建。一个类可以继承一个或多个类,继承一个类称为单继承,继承多个类称为多继承。被继承的类称为父类,也称为超类或基类,继承的类称为子类或派生类。

    继承的实现

    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 等等 这就是互称为鸭子类型。
    
    鸭子类型
  • 相关阅读:
    【BZOJ1023】仙人掌图(SHOI2008)-圆方树+DP+单调队列
    【BZOJ4816】数字表格(SDOI2017)-莫比乌斯反演+数论分块
    【BZOJ3529】数表(SDOI2014)-莫比乌斯反演+树状数组
    【BZOJ3714】Kuglarz(PA2014)-最小生成树
    javascript div元素后追加节点
    php多文本框提交
    有几数组表单,js怎么获得数组并动态相加输出到文本框
    SqlCommand.Parameters.add()方法
    ASP.net后台弹出消息对话框的方法!【转】
    Access中的SELECT @@IDENTITY
  • 原文地址:https://www.cnblogs.com/zzliu/p/10273003.html
Copyright © 2011-2022 走看看