python 全栈 python基础 (十六)面向对象编程的 继承 多态与多态性 封装

一、继承顺序:

  多继承情况下,有两种方式:深度优先和广度优先

1、py3/py2 新式类的继承:在查找属性时遵循:广度优先

  继承顺序是多条分支,按照从左往右的顺序,进行一步一步查找,一个分支走完会走另一个分支(若多条分支汇总一个头,除最后一条分支能走到头,其他的都走到次之位置停止,进行下一条分支的查找),直到查找到头为止。

可以利用 类名.__mro__ 的方法查看类之间的继承关系(经典类没有这种方法)

 1 class B(object):
 2     def func(self):
 3         print(‘-----> B‘)
 4     pass
 5 class C(object):
 6     def func(self):
 7         print(‘-----> C‘)
 8     pass
 9 class D(B,C):
10     def func(self):
11         print(‘-----> D‘)
12     pass
13 class E(B,C):
14     def func(self):
15         print(‘-----> E‘)
16     pass
17 class F(D,E):
18     def func(self):
19         print(‘-----> F‘)
20     pass
21 # f=F()
22 # f.func()
23 print(F.__mro__)
24
25 #执行结果:
26 (<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.E‘>, <class ‘__main__.B‘>, <class ‘__main__.C‘>, <class ‘object‘>)

 2、py2 经典类的继承:在查找属性时遵循:深度优先

  一个子类继承多个父类,可以看作是多个分支,依然是遵循从左到右的顺序,只是第一条分支从尾走到头,找不到就会再走别的分支,只是最开始的父类不会再找。

3、继承原理
  python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

合并所有父类的MRO列表遵循如下三条准则:
  1.子类会先于父类被检查
  2.多个父类会根据它们在列表中的顺序被检查
  3.如果对下一个类存在两个合法的选择,选择第一个父类

子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法()
方法二:super()

  两种方式,虽然都能调用,但是方法一存在极大的局限性,需要明确父类名,假如父类不存在,直接结果就会导致子类调用执行时报错,而super()是按照新式类的继承顺序,利用 super().函数名(参数) 的方式,继承父类方法。

  使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次。

  注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

super在python2中的用法: 1:super(自己的类,self).父类的函数名字 2:super只能用于新式类
 1 #coding:utf-8
 2 #super在python2中的用法:
 3     # 1:super(自己的类,self).父类的函数名字
 4     # 2:super只能用于新式类
 5 class People(object):
 6     def __init__(self,name,sex,age):
 7         self.name=name
 8         self.age=age
 9         self.sex=sex
10     def walk(self):
11         print(‘%s is walking‘ %self.name)
12 class Chinese(People):
13     country=‘China‘
14     def __init__(self,name,sex,age,language=‘Chinese‘):
15         # self.name=name
16         # self.sex=sex
17         # self.age=age
18         # People.__init__(self,name,sex,age)
19         super(Chinese,self).__init__(name,sex,age)
20         self.language=language
21 c=Chinese(‘egon‘,‘male‘,18)
22 print c.name,c.age,c.sex,c.language
23
24 #执行结果:
25 egon 18 male Chinese


 1 #在python3中
 2 class People:
 3     def __init__(self,name,sex,age):
 4         self.name=name
 5         self.age=age
 6         self.sex=sex
 7     def walk(self):
 8         print(‘%s is walking‘ %self.name)
 9 class Chinese(People):
10     country=‘China‘
11     def __init__(self,name,sex,age,language=‘Chinese‘):
12         super().__init__(name,sex,age)#super()函数.父类函数名(参数)
13         # 由于super()相当于是带入一个类而不在是函数,所以传值的时候,不再需要给self传值,实例化的时候自动带入。
14         self.language=language
15     def walk(self,x):
16         super().walk()  #super()函数.父类函数名()
17         print(‘%s is chase %s‘%(self.name,x))
18 c=Chinese(‘anyone‘,‘male‘,18)
19 print(c.name,c.age,c.sex,c.language)
20 c.walk(‘somebody‘)
21
22 #执行结果:
23 anyone 18 male Chinese
24 anyone is walking
25 anyone is chase somebody

二、多态和多态性:

1、定义:

  多态(是从定义角度出发):同一类事物的多种形态。(一个抽象类有多个子类,因而多态的概念依赖于继承)例如:动物的多种形态:人,狗,猪。

  多态性(是从使用角度出发):同一种调用方式,不同的执行效果。具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。

  多态性依赖于:1.继承;2.定义的接口
  #定义统一的接口,可以传入不同类型的值,但是调用的逻辑都一样,但是执行的结果却不一样。

 1 #多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度)
 2 class Animal:
 3     def run(self):
 4         raise AttributeError(‘子类必须实现这个方法‘)
 5
 6 class People(Animal):
 7     def run(self):
 8         print(‘人正在走‘)
 9
10 class Pig(Animal):
11     def run(self):
12         print(‘pig is walking‘)
13
14 class Dog(Animal):
15     def run(self):
16         print(‘dog is running‘)
17
18 peo1=People()
19 pig1=Pig()
20 d1=Dog()
21 #实例化调用方法得到的结果
22 # peo1.run()
23 # pig1.run()
24 # d1.run()
25
26 #多态性:一种调用方式,不同的执行效果(多态性)
27 # 多态性依赖于:
28 #     1.继承
29 #     2.
30 ##多态性:定义统一的接口,
31 def func(obj): #obj这个参数没有类型限制,可以传入不同类型的值
32     obj.run() #调用的逻辑都一样,执行的结果却不一样
33
34 func(peo1)
35 func(pig1)
36 func(d1)
37
38 #执行结果:
39 人正在走
40 pig is walking
41 dog is running

  多态性的实质:实质就是定义了一个函数接口,在这个函数中定义了所有类内通性的功能,只要传入参数(对象名)函数调用执行,就得到不同的结果。这就是所谓的一种调用方式,不同的执行结果。

2、 多态性的好处:
 1、增加了程序的灵活性
  以不变应万变,不论对象千变万化,使用者都是同一种形式去调用
 2、增加了程序的可扩展性
  通过继承父类创建了一个新的子类,使用者无需更改自己的代码,还是用定义的接口函数去调用。     

三、封装:
  1、封装的本质就是隐藏,将一些复杂的执行过程隐藏起来,留下调用的接口(接口就是函数,称为接口函数;一组接口函数的集合体构成一个接口),我们通过这些接口进行交互,不管程序在内部怎么个应用流转方式,只为得到最后的结果。

 2、数据封装主要原因是:保护隐私方法封装主要原因是:隔离复杂度

3、封装分为两个层面:

  但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口

第一层面的封装:其实就是创建类或是对象,通过类名.或对象名.的方式调用对应的方法,这本身就是一种封装。
  注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口

第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

  1、在python中用双下划线的方式实现隐藏属性(设置成私有的)。类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

 1 class A:
 2     __x =1 #_A__x
 3     def __test(self): #_A__test
 4         print(‘from A‘)
 5 print(A.__dict__)  #在方法空间中查看变形的属性
 6 #print(A.__x) #用这种方法无法调用到变量,已变形
 7 print(A._A__x)  #若想强行访问,正确的调用方式
 8 a = A()  #实例化
 9 print(a.__dict__)
10 print(a._A__x)  #对象的调用
11
12 A._A__test(123)  #强制访问,类的调用
13 a._A__test()    #强制访问,对象的调用
14
15 #执行结果:
16 {‘__module__‘: ‘__main__‘, ‘_A__x‘: 1, ‘_A__test‘: <function A.__test at 0x000000000292C9D8>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘A‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘A‘ objects>, ‘__doc__‘: None}
17 1
18 {}
19 1
20 from A
21 from A  

  2、注意:__名字,这种语法只在定义的时候才会有变形的效果,如果类或者对象已经产生了,就不会有变形效果。

  变形的过程只在类的定义时发生一次;在定义后的赋值操作,就不会变形
#注意:__名字,这种语法只在定义的时候才会有变形的效果,如果类或者对象已经产生了,就不会有变形效果。
#变形的过程只在类的定义时发生一次;在定义后的赋值操作,就不会变形
#1、内部定义
class A:
    def __init__(self):
        self.__x =1    #_A__x 定义在产生的过程中,肯定会变形
    def tell(self):   #在类内部定义的变形变量(隐藏),需要定义一个接口函数来实现访问,外部去访问这个接口进行调用
        print(self.__x) #在类内部可以直接用__名字调用,来访问到变形的属性
a = A()
print(a.__dict__) #在类的名称空间中查看,已变形
#print(a.__x)  #属性已经变形,调用不到
a.tell()

#执行结果:
{‘_A__x‘: 1}
1

#2、外部定义
class B:
    pass

B.__x =1 #给类添加一个为__x的变量,而不是变形
print(B.__dict__)  #在类的名称空间中查看,未变形
print(B.__x)   #打印结果

b = B()  #类实例化
b.__x =1  #为对象添加一个为__x的变量,而不是变形
print(b.__dict__) #查看对象的名称空间
print(b.__x)  #打印结果

#执行结果:
{‘__module__‘: ‘__main__‘, ‘__dict__‘: <attribute ‘__dict__‘ of ‘B‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘B‘ objects>, ‘__doc__‘: None, ‘__x‘: 1}
1
{‘__x‘: 1}
1
3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
# 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
# 1、正常情况下:
class A:
    def fa(self):
        print(‘from A‘)
    def test(self):
        self.fa()
class B(A):
    def fa(self):
        print(‘from B‘)
b = B()
b.test() #b.test--->B--->A 得到b.fa() 然后在对象b中找--->类B找--->父类A找

# 执行结果:
from B

# 注意:时刻谨记:在定义阶段就会变形
# 2、把fa定义成私有的,即__fa
class A:
    def __fa(self):   #_A__fa
        print(‘from A‘)
    def test(self):  #在定义阶段就会变形
        self.__fa()   #self._A__fa
class B(A):
    def __fa(self):   #_B__fa
        print(‘from B‘)
b = B()  #实例化
b.test()  #自己的对象和类名称空间没有,在父类的名称空间找到,函数已经变形。

#执行结果:
from A
时间: 2024-08-02 02:45:38

python 全栈 python基础 (十六)面向对象编程的 继承 多态与多态性 封装的相关文章

python 全栈开发,Day30(第一次面向对象考试)

月考题: python 全栈11期月考题 一 基础知识:(70分) 1.文件操作有哪些模式?请简述各模式的作用(2分) 2.详细说明tuple.list.dict的用法,以及它们的特点(3分) 3.解释生成器(generator)与函数的不同,并实现且使用简单generator(3分) 4.如何理解lambda函数/表达式(2分) 5.a=10 b=20 def test(a,b): print(a,b) c = test(b,a) print(c) 上述代码中,打印出来的值a,b,c分别是什么

Python全栈开发之9、面向对象、元类以及单例

前面一系列博文讲解的都是面向过程的编程,如今是时候来一波面向对象的讲解了 一.简介 面向对象编程是一种编程方式,使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用.类就是一个模板,模板里可以包含多个方法(函数),方法里实现各种各样的功能,,对象则是根据模板创建的实例,通过实例,对象可以执行类中的方法,每个对象都拥有相同的方法,但各自的数据可能不同. 二.类.对象和方法 在Python中,定义类是通过class关键字,class后面紧接着是类名,类名通常

Python全栈之路系列----之-----面向对象

面向对象基本介绍 python编程方式: 面向过程编程  (流水线式思维) 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可. 缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身. 面向函数编程 封装代码,简化流程 但是本质上没有改变结果   是面向结果编程思想 面向对象编程 优点是:解决了程序的扩展性.对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易. 缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精

python全栈开发基础【第二十六篇】(concurrent.futures模块、协程、Greenlet、Gevent)

注意 1.不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 2.只要你用并发,就会有锁的问题,但是你不能一直去自己加锁吧那么我们就用QUEUE,这样还解决了自动加锁的问题由Queue延伸出的一个点也非常重要的概念.以后写程序也会用到这个思想.就是生产者与消费者问题 一.Python标准模块--concurrent.futures(并发未来) concurent.future模块需要了解的 1.concurent

python 全栈 python基础(六)前期基础整理

计算机中,有且仅有CPU具有执行权限,能够执行指令的只有CPU! 人在应用程序上输入的文字或文本,叫做明文! 在屏幕上输入或是输出的是字符,计算机保存的是 有字符编码的 二进制数. 变量赋值规则:例如:a=1 先在内存中开辟一块空间,存放数1,内存上会有一个相对应的id号,然后变量a指向这块内存区域和id号,python编译器有垃圾清除的功能,若开辟的这块内存区域一段时间内不调用,数据就会被清除,为其他的数据腾空间. python2 容错率高,自作聪明的完成转码拼接. python3 将字节类型

python全栈开发基础【第十九篇】进程

一.什么是进程 进程:正在进行的一个过程或是一个任务.而负责执行任务的是CPU. 举例:(单核+多道,实现多个进程的并发): 比如说你就是一个CPU,你下午有几个活要干,吃饭,洗衣服,上厕所等.但是就在那一下午要把所有的事干完(而CPU同一时间只能干一件事),那么如何才能让多个任务实现并发执行的效果呢?那么,你应该这样做,你可以先做饭,在等待饭熟的过程中你可以去洗个衣服,洗的差不多饭也就熟了,那么你在去上个厕所也可以嘛. 二.进程与程序的区别 程序仅仅只是一堆代码而已,而进程指的是程序的运行过程

python全栈开发基础【第十八篇】网络编程(socket)

一.网络协议 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构(互联网中处处是C/S架构):B/S架构也是C/S架构的一种,B/S是浏览器/服务器 C/S架构与socket的关系:我们用socket就是为了完成C/S架构的开发 osi七层 引子: 须知一个完整的计算机系统是由硬件.操作系统.应用软件三者组成,具备了这三个条件,一台计算机系统就可以自己跟自己玩了(打个单机游戏,玩个扫雷啥的) 如果你要跟别人一起玩,那你就需要上网了,什么是互联网? 互联网的核心就是由一堆协议组成

python全栈开发基础【第二十二篇】进程池和回调函数

一.数据共享 1.进程间的通信应该尽量避免共享数据的方式 2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实上Manager的功能远不止于此. 命令就是一个程序,按回车就会执行(这个只是在windows情况下) tasklist 查看进程 tasklist | findstr pycharm #(findstr是进行过滤的),|就是管道(tasklist执行的内容就放到管道里面了, 管道后面的findst

python全栈开发基础【第二十五篇】死锁,递归锁,信号量,Event事件,线程Queue

一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程, 如下就是死锁 #死锁现象 死锁------------------- from threading import Thread,Lock,RLock import time mutexA = Lock() mutexB = Lock() class