PythonI/O进阶学习笔记_3.2面向对象编程_python的继承(多继承

系统 176 0

前言:

本篇相关内容分为3篇多态、继承、封装,这篇为第二篇 继承。

本篇内容围绕 python基础教程这段:
在面向对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法。使用对象而非全局变量和函数的原因有多个,下面列出了使用对象的最重要的好处。
 多态:可对不同类型的对象执行相同的操作,而这些操作就像“被施了魔法”一样能够正常运行。
 封装:对外部隐藏有关对象工作原理的细节。
 继承:可基于通用类创建出专用类。
内容较多,这篇为中篇。

Content:

- 继承

1. 什么是继承,继承的作用和常用状态?

2. python的多继承、instance和type?

3. python中的super函数

4. python的MRO查找机制来对应多继承和super

5. python的抽象基类

6. django等大大框架和python源码中最常用的Mixin模式多继承实例

 (下篇

- 封装

1.数据封装和私有属性

2. 类变量和实例变量(对象变量)

3. 类属性和实例属性得查找顺序(MRO)

4. 静态方法 类方法和对象方法使用以及参数

5. python的接口和自省机制

6. 上下文管理器

 )

 

一 继承

1.什么是继承?继承有哪些作用?常用?

    - 继承的概念  :在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

            
              //INPUT:
              
class
Animals(): def say(self): print ( " say something " ) def eat(self): print ( " eat something " ) class Duck(Animals): def say(self): print ( " gaga " ) class Dog(Animals): def say(self): print ( " wangwang " ) class Miao(Animals): def say(self): print ( " miaomiao " ) for i in [Duck(),Dog(),Miao()]: i.eat()

//OUTPUY:
eat something
eat something
eat something

    - 继承的作用:可以解决代码重用的问题。

    - 比较常用的应该就是在python相关web框架里这种比较比较复杂的框架,和许多第三方库的类。天知道这些大牛的框架里面默默有效率的帮我们做了多少事- -。天知道我们直接用他们的方法有多方便。

 

2.python多继承需要注意哪些?用instance和type来判断python类继承的关系。

a.多继承需要注意的地方:

- 类继承的顺序和方法在多继承类中查找的顺序

    --.子类继承父类时,在子类进行属性调用的顺序为:先查找自己的属性字典,若自己的属性字典中无该属性,则会依次按照继承父类的顺序来依次查找父类的属性字典;

    --.子类继承父类,当父类和子类均有相同的属性时,子类并不会影响父类的属性。

总结起来就是: 按继承的顺序来依次查询属性,一旦查到则停止;子类和父类的属性相互独立,互不影响;子类可以调用父类的属性,反之不行

b.用instance和type来判断两个类的关系

instance和type都是用来判断参数1是否是参数2类型,区别在于是否会考虑继承关系。

isinstance:
 
type:
type(b)是:
由上可得,type用于判断对象,而isinstance判断是否在继承链里面。
 
3.python的super函数
a.super函数干嘛的?为什么要用super?
实际上是用来调用自己的父类的。
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
 
b.super的用法
在python2中,super(B,self).__init__()
python3中 super().__init__()
                
                  #
                
                
                  Python3.x 实例:
                
                
                  class
                
                
                   A:
     
                
                
                  def
                
                
                   add(self, x):
         y 
                
                = x+1
         
                
                  print
                
                
                  (y)

                
                
                  class
                
                
                   B(A):
    
                
                
                  def
                
                
                   add(self, x):
        super().add(x)
b 
                
                =
                
                   B()
b.add(
                
                2)  
                
                  #
                
                
                   3
                
                
# Python2.x 实例: # !/usr/bin/python # -*- coding: UTF-8 -*- class A(object): # Python2.x 记得继承 object def add(self, x): y = x+1 print (y) class B(A): def add(self, x): super(B, self).add(x) b = B() b.add( 2) # 3

 

4.super和多继承时查找父类的顺序 MRO C3算法
a.MRO C3算法

C3线性是用于获取多重继承下继承顺序的一种算法。通常,被称为 方法解析顺序 ,即MRO(method resolution order)。

算法的名字“C3”并不是缩写,而是指该算法的三大重要属性:

- 前趋图。作为有向无环图,找不到任何的循环,通常用前趋图来理解程序的依赖关系。

- 保持局部的优先次序。

- 单调性。

C3是1996年首次被提出。在python2.3及后续版本中,C3被选定为默认的解析算法。

一个类的C3线性表,是由两部分进行merge操作得到的,第一部分是是它所有父类的C3线性表(parents' linearizations),第二部分是它所有父类组成的列表(parents list)。后者其实是局部的优先级列表。

在C3被应用之前,广度优先和深度优先是被应用于解析顺序的。

C3算法计算方法:有点类似拓扑排序(有向图)      想深刻了解 C3 还是自己查吧...下面有段简单的多继承代码和其对应的拓扑排序的抽象图 ( 所用代码实例和图片均来应用自别处,文章末尾有链接 )

                  
                    class
                  
                  
                     D(object):
    
                  
                  
                    pass
                  
                  
                    class
                  
                  
                     E(object):
    
                  
                  
                    pass
                  
                  
                    class
                  
                  
                     F(object):
    
                  
                  
                    pass
                  
                  
                    class
                  
                  
                     C(D, F):
    
                  
                  
                    pass
                  
                  
                    class
                  
                  
                     B(E, D):
    
                  
                  
                    pass
                  
                  
                    class
                  
                  
                     A(B, C):
    
                  
                  
                    pass
                  
                  
                    if
                  
                  
                    __name__
                  
                   == 
                  
                    '
                  
                  
                    __main__
                  
                  
                    '
                  
                  
                    :
    
                  
                  
                    print
                  
                   A.
                  
                    __mro__
                  
                

得到的输出结果:

                  (<
                  
                    class
                  
                  
                    '
                  
                  
                    __main__.A
                  
                  
                    '
                  
                  >, <
                  
                    class
                  
                  
                    '
                  
                  
                    __main__.B
                  
                  
                    '
                  
                  >, <
                  
                    class
                  
                  
                    '
                  
                  
                    __main__.E
                  
                  
                    '
                  
                  >, <
                  
                    class
                  
                  
                    '
                  
                  
                    __main__.C
                  
                  
                    '
                  
                  >, <
                  
                    class
                  
                  
                    '
                  
                  
                    __main__.D
                  
                  
                    '
                  
                  >, <
                  
                    class
                  
                  
                    '
                  
                  
                    __main__.F
                  
                  
                    '
                  
                  >, 
                  
                    
                      '
                    
                    
                      object
                    
                    
                      '
                    
                    >)
                  
                
  • 下面就是抽象出来的图


我们就用拓扑排序来分析,但是这里会碰到同时存在好几个点都是入度为0 (说人话,就是没有被别人指向的点),这时按照树的排序来,即 从左到右,从根到叶 ,这里 A 就是根。

所以具体情况就是:我们先找到了点 A只有它没有被别人指向,输出 A ;去掉A及其伸出的两条线,剩 B 和 C 点同时满足只指向别人,按照树的顺序从左到右,故先输出 B ;去掉线剩 E 和 C ,输出 E ;去线剩 C,输出 C ;去线剩 D 和 F ,输出 D ;去线只剩F ,输出 F ;最后输出 object ;得到的输出结果:  A B E C D F object

对比系统打印出的结果,顺序是一致的。这样下次你就可以装逼地手动计算多继承时调用类的顺序了


以上结论来源链接:https://www.jianshu.com/p/6651ed35104c

b.用__init__和超类的super().__init__看子类和父类的构造函数关系。
现在有一个继承关系,SonBird继承Bird。但是子类和父类都有自己的构造函数。
              
                class
              
              
                 Bird:
    
              
              
                def
              
              
                __init__
              
              
                (self):
          self.hungry 
              
              =
              
                 True
    
              
              
                def
              
              
                 eat(self):
          
              
              
                if
              
              
                 self.hungry:
               
              
              
                print
              
              
                '
              
              
                Ahahahah
              
              
                '
              
              
                else
              
              
                :
               
              
              
                print
              
              
                '
              
              
                No thanks!
              
              
                '
              
              
                class
              
              
                 SongBird(Bird):
     
              
              
                def
              
              
                __init__
              
              
                (self):
          self.sound 
              
              = 
              
                '
              
              
                Squawk
              
              
                '
              
              
                def
              
              
                 sing(self):
          
              
              
                print
              
              
                 self.song()

sb 
              
              =
              
                 SongBird()
sb.sing()    
              
              
                #
              
              
                 能正常输出
              
              
sb.eat()     
              
                #
              
              
                 报错,因为 songgird 中没有 hungry 特性
              
            

这时候,如果需要继承父类构造函数里的属性,其实是可以有两种方法的。

第一种 - 调用未绑定的超类构造方法(多用于旧版 python 阵营)

              
                class
              
              
                 SongBird(Bird):
     
              
              
                def
              
              
                __init__
              
              
                (self):
          Bird.
              
              
                __init__
              
              
                (self)
          self.sound 
              
              = 
              
                '
              
              
                Squawk
              
              
                '
              
              
                def
              
              
                 sing(self):
          
              
              
                print
              
               self.song()
            

原理 :在调用了一个实例的方法时,该方法的self参数会自动绑定到实例上(称为绑定方法);如果直接调用类的方法(比如Bird.__init__),那么就没有实例会被绑定,可以自由提供需要的self参数(未绑定方法)。

第二种 - 使用super函数(只在新式类中有用)

              
                class
              
              
                 SongBird(Bird):
     
              
              
                def
              
              
                __init__
              
              
                (self):
          super(SongBird,self).
              
              
                __init__
              
              
                ()
          self.sound 
              
              = 
              
                '
              
              
                Squawk
              
              
                '
              
              
                def
              
              
                 sing(self):
          
              
              
                print
              
               self.song()
            

原理: 它会查找所有的超类,以及超类的超类,直到找到所需的特性为止。

 
5.python的抽象基类(abc)
a.什么是抽象基类? 抽象基类的特性?
  抽象基类的作用类似于JAVA中的接口。在接口中定义各种方法,然后继承接口的各种类进行具体方法的实现。抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
抽象基类的特性:
- 无法用来实例化。
 
 
b.为什么要有抽象基类?直接用鸭子类型不就好了?
第一个需求:在某些情况下,我们需要判定某个对象的类型。
第二个需求:我们需要强制某个子类必须实现某些方法
 
c.抽象基类的应用
抽象类是不能(至少是不应该)实例化的类,其职责是定义子类应实现的一组抽象方法。
下面是一个简单的示例:使用@abstractmethod来将方法标记为抽象的——在子类中必须实现的方法。
              
                from
              
               abc 
              
                import
              
              
                 ABC, abstractmethod 

              
              
                class
              
              
                 Talker(ABC): 
  @abstractmethod 
  
              
              
                def
              
              
                 talk(self): 
  
              
              
                pass
              
            

结果:

              ##抽象基类不能被实例化
              
>>> Talker() Traceback (most recent call last): File " " , line 1, in TypeError: Can ' t instantiate abstract class Talker with abstract methods talk
              
                ##没有重写方法的时候,继承抽象基类的类 本质也是抽象类
                
class
Knigget(Talker): pass ##由于没有重写方法talk,因此这个类也是抽象的,不能实例化。如果你试图这样做,将出现类似于前面的错误消息。
              
                ###继承后重写了方法就没什么问题
                
class
Knigget(Talker): def talk(self): print ( " Ni! " )

##output:
>>> k = Knigget()
>>> isinstance(k, Talker)
True 1
>>> k.talk()
Ni!

 

6.混入模式:多继承应用实例(Mixin 模式)
首先,Mixin模式在python应用中十分常见,包括openstack、django等,有大量的Mixin模式应用。想要进阶,就需要理解为什么大部分框架都会用这种模式。
详细的谷歌大神关于动态语言的设计模式可以看: http://norvig.com/design-patterns/ppframe.htm 或者《松本行弘的程序世界》里有介绍ruby的混入模式和python一样。
 
从某种程度上来说,继承强调 I am,Mixin 强调 I can。
 
以下来自: https://www.liaoxuefeng.com/wiki/897692888725344/923030524000032

在设计类的继承关系时,通常,主线都是单一继承下来的,例如, Ostrich 继承自 Bird 。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让 Ostrich 除了继承自 Bird 外,再同时继承 Runnable 。这种设计通常称之为Mixin。

为了更好地看出继承关系,我们把 Runnable Flyable 改为 RunnableMixin FlyableMixin 。类似的,你还可以定义出肉食动物 CarnivorousMixin 和植食动物 HerbivoresMixin ,让某个动物同时拥有好几个Mixin:

          
            
              
                class 
                
                  Dog
                  
                    (Mammal, RunnableMixin, CarnivorousMixin):
    
                    
                      pass 
                    
                  
                
              
            
          
        

Mixin的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系。

Python自带的很多库也使用了Mixin。举个例子,Python自带了 TCPServer UDPServer 这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由 ForkingMixin ThreadingMixin 提供。通过组合,我们就可以创造出合适的服务来。

比如,编写一个多进程模式的TCP服务,定义如下:

          
            
              
                class 
                
                  MyTCPServer
                  
                    (TCPServer, ForkingMixin):
    
                    
                      pass 
                    
                  
                
              
            
          
        

编写一个多线程模式的UDP服务,定义如下:

          
            
              
                class 
                
                  MyUDPServer
                  
                    (UDPServer, ThreadingMixin):
    
                    
                      pass 
                    
                  
                
              
            
          
        

如果你打算搞一个更先进的协程模型,可以编写一个 CoroutineMixin

          
            
              
                class 
                
                  MyTCPServer
                  
                    (TCPServer, CoroutineMixin):
    
                    
                      pass 
                    
                  
                
              
            
          
        

这样一来,我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。

 


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请请扫描上面二维码支持博主1元、2元、5元等您想捐的金额吧,狠狠点击下面给点支持吧

发表我的评论
最新评论 总共0条评论