python的两种继承模式_PythonI/O进阶学习笔记_3.2面向对象编程_python的继承(多继承/super/MRO/抽象基类/mixin模式)...

前言:

本篇相关内容分为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:

classAnimals():defsay(self):print("say something")defeat(self):print("eat something")classDuck(Animals):defsay(self):print("gaga")classDog(Animals):defsay(self):print("wangwang")classMiao(Animals):defsay(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 实例:

classA:defadd(self, x):

y= x+1

print(y)classB(A):defadd(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

defadd(self, x):

y= x+1

print(y)classB(A):defadd(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 还是自己查吧...下面有段简单的多继承代码和其对应的拓扑排序的抽象图 (所用代码实例和图片均来应用自别处,文章末尾有链接)

classD(object):pass

classE(object):pass

classF(object):pass

classC(D, F):pass

classB(E, D):pass

classA(B, C):pass

if __name__ == '__main__':print A.__mro__

得到的输出结果:

(, , , , , , )

下面就是抽象出来的图

我们就用拓扑排序来分析,但是这里会碰到同时存在好几个点都是入度为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。但是子类和父类都有自己的构造函数。

classBird:def __init__(self):

self.hungry=Truedefeat(self):ifself.hungry:print 'Ahahahah'

else:print 'No thanks!'

classSongBird(Bird):def __init__(self):

self.sound= 'Squawk'

defsing(self):printself.song()

sb=SongBird()

sb.sing()#能正常输出

sb.eat() #报错,因为 songgird 中没有 hungry 特性

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

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

classSongBird(Bird):def __init__(self):

Bird.__init__(self)

self.sound= 'Squawk'

defsing(self):print self.song()

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

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

classSongBird(Bird):def __init__(self):

super(SongBird,self).__init__()

self.sound= 'Squawk'

defsing(self):print self.song()

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

5.python的抽象基类(abc)

a.什么是抽象基类? 抽象基类的特性?

抽象基类的作用类似于JAVA中的接口。在接口中定义各种方法,然后继承接口的各种类进行具体方法的实现。抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。

抽象基类的特性:

- 无法用来实例化。

b.为什么要有抽象基类?直接用鸭子类型不就好了?

第一个需求:在某些情况下,我们需要判定某个对象的类型。

第二个需求:我们需要强制某个子类必须实现某些方法

c.抽象基类的应用

抽象类是不能(至少是不应该)实例化的类,其职责是定义子类应实现的一组抽象方法。

下面是一个简单的示例:使用@abstractmethod来将方法标记为抽象的——在子类中必须实现的方法。

from abc importABC, abstractmethodclassTalker(ABC):

@abstractmethoddeftalk(self):pass

结果:

##抽象基类不能被实例化

>>>Talker()

Traceback (most recent call last):

File"", line 1, in TypeError: Can't instantiate abstract class Talker with abstract methods talk

##没有重写方法的时候,继承抽象基类的类 本质也是抽象类

classKnigget(Talker):pass##由于没有重写方法talk,因此这个类也是抽象的,不能实例化。如果你试图这样做,将出现类似于前面的错误消息。

###继承后重写了方法就没什么问题

classKnigget(Talker):deftalk(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。

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值