Py修行路 python基础 (十六)面向对象编程的 继承 多态与多态性 封装
一、继承顺序:
多继承情况下,有两种方式:深度优先和广度优先
1、py3/py2 新式类的继承:在查找属性时遵循:广度优先
继承顺序是多条分支,按照从左往右的顺序,进行一步一步查找,一个分支走完会走另一个分支(若多条分支汇总一个头,除最后一条分支能走到头,其他的都走到次之位置停止,进行下一条分支的查找),直到查找到头为止。
可以利用 类名.__mro__ 的方法查看类之间的继承关系(经典类没有这种方法)
class B(object):
def func(self):
print('-----> B')
pass
class C(object):
def func(self):
print('-----> C')
pass
class D(B,C):
def func(self):
print('-----> D')
pass
class E(B,C):
def func(self):
print('-----> E')
pass
class F(D,E):
def func(self):
print('-----> F')
pass
# f=F()
# f.func()
print(F.__mro__) #执行结果:
(<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只能用于新式类
#coding:utf-8
#super在python2中的用法:
# 1:super(自己的类,self).父类的函数名字
# 2:super只能用于新式类
class People(object):
def __init__(self,name,sex,age):
self.name=name
self.age=age
self.sex=sex
def walk(self):
print('%s is walking' %self.name)
class Chinese(People):
country='China'
def __init__(self,name,sex,age,language='Chinese'):
# self.name=name
# self.sex=sex
# self.age=age
# People.__init__(self,name,sex,age)
super(Chinese,self).__init__(name,sex,age)
self.language=language
c=Chinese('egon','male',18)
print c.name,c.age,c.sex,c.language #执行结果:
egon 18 male Chinese
#在python3中
class People:
def __init__(self,name,sex,age):
self.name=name
self.age=age
self.sex=sex
def walk(self):
print('%s is walking' %self.name)
class Chinese(People):
country='China'
def __init__(self,name,sex,age,language='Chinese'):
super().__init__(name,sex,age)#super()函数.父类函数名(参数)
# 由于super()相当于是带入一个类而不在是函数,所以传值的时候,不再需要给self传值,实例化的时候自动带入。
self.language=language
def walk(self,x):
super().walk() #super()函数.父类函数名()
print('%s is chase %s'%(self.name,x))
c=Chinese('anyone','male',18)
print(c.name,c.age,c.sex,c.language)
c.walk('somebody') #执行结果:
anyone 18 male Chinese
anyone is walking
anyone is chase somebody
二、多态和多态性:
1、定义:
多态(是从定义角度出发):同一类事物的多种形态。(一个抽象类有多个子类,因而多态的概念依赖于继承)例如:动物的多种形态:人,狗,猪。
多态性(是从使用角度出发):同一种调用方式,不同的执行效果。具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
多态性依赖于:1.继承;2.定义的接口
#定义统一的接口,可以传入不同类型的值,但是调用的逻辑都一样,但是执行的结果却不一样。
#多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度)
class Animal:
def run(self):
raise AttributeError('子类必须实现这个方法') class People(Animal):
def run(self):
print('人正在走') class Pig(Animal):
def run(self):
print('pig is walking') class Dog(Animal):
def run(self):
print('dog is running') peo1=People()
pig1=Pig()
d1=Dog()
#实例化调用方法得到的结果
# peo1.run()
# pig1.run()
# d1.run() #多态性:一种调用方式,不同的执行效果(多态性)
# 多态性依赖于:
# 1.继承
# 2.
##多态性:定义统一的接口,
def func(obj): #obj这个参数没有类型限制,可以传入不同类型的值
obj.run() #调用的逻辑都一样,执行的结果却不一样 func(peo1)
func(pig1)
func(d1) #执行结果:
人正在走
pig is walking
dog is running
多态性的实质:实质就是定义了一个函数接口,在这个函数中定义了所有类内通性的功能,只要传入参数(对象名)函数调用执行,就得到不同的结果。这就是所谓的一种调用方式,不同的执行结果。
2、 多态性的好处:
1、增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用
2、增加了程序的可扩展性
通过继承父类创建了一个新的子类,使用者无需更改自己的代码,还是用定义的接口函数去调用。
三、封装:
1、封装的本质就是隐藏,将一些复杂的执行过程隐藏起来,留下调用的接口(接口就是函数,称为接口函数;一组接口函数的集合体构成一个接口),我们通过这些接口进行交互,不管程序在内部怎么个应用流转方式,只为得到最后的结果。
2、数据封装主要原因是:保护隐私;方法封装主要原因是:隔离复杂度。
3、封装分为两个层面:
但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口
第一层面的封装:其实就是创建类或是对象,通过类名.或对象名.的方式调用对应的方法,这本身就是一种封装。
注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
1、在python中用双下划线的方式实现隐藏属性(设置成私有的)。类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
class A:
__x =1 #_A__x
def __test(self): #_A__test
print('from A')
print(A.__dict__) #在方法空间中查看变形的属性
#print(A.__x) #用这种方法无法调用到变量,已变形
print(A._A__x) #若想强行访问,正确的调用方式
a = A() #实例化
print(a.__dict__)
print(a._A__x) #对象的调用 A._A__test(123) #强制访问,类的调用
a._A__test() #强制访问,对象的调用 #执行结果:
{'__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}
1
{}
1
from A
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
Py修行路 python基础 (十六)面向对象编程的 继承 多态与多态性 封装的更多相关文章
- Py修行路 python基础(六)前期基础整理
计算机中,有且仅有CPU具有执行权限,能够执行指令的只有CPU! 人在应用程序上输入的文字或文本,叫做明文! 在屏幕上输入或是输出的是字符,计算机保存的是 有字符编码的 二进制数. 变量赋值规则:例如 ...
- Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类
一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...
- Py修行路 python基础 (十四)递归 及 面向对象初识及编程思想
一.递归 1.定义: 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. (1)递归就是在过程或函数里调用自身: (2)在使用递归策略时,必须有一个明确的递归结束条件 ...
- Py修行路 python基础 (十九)面向对象进阶(下)
item系列 __slots__方法 __next__ 和 __iter__实现迭代器 析构函数 上下文管理协议 元类一.item系列 把对象操作属性模拟成字典的格式. 例如:对象名['key'] ...
- Py修行路 python基础 (二十五)线程与进程
操作系统是用户和硬件沟通的桥梁 操作系统,位于底层硬件与应用软件之间的一层 工作方式:向下管理硬件,向上提供接口 操作系统进行切换操作: 把CPU的使用权切换给不同的进程. 1.出现IO操作 2.固定 ...
- Py修行路 python基础 (十八) 反射 内置attr 包装
一.isinstance 和 issubclass1.isinstance(obj,cls)检查是否obj是否是类 cls 的对象.2.issubclass(sub, super)检查sub类是否是 ...
- Py修行路 python基础 (二十)模块 time模块,random模块,hashlib模块,OS及sys模块
一.前提介绍: 可以开辟作用域的只有类,函数,和模块 for循环 if,else: 不能开辟自己的作用域 避免程序复用和重复调用,将这些写到一个.py文件中,做成一个模块,进行调 ...
- Py修行路 python基础 (十二) 协程函数应用 列表生成式 生成器表达式
一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._next_() 取下一个值 优点: 1.提供了 ...
- Py修行路 python基础 (二十四)socket编程
socket编程 一.客户端/服务端架构 客户端/服务端架构 即C/S架构,包括:1.硬件C/S架构,2.软件C/S架构. 互联网中处处都是C/S架构,学习socket 就是为了完成C/S架构的开发. ...
随机推荐
- 使用科大讯飞时碰上的问题(unity调用Android)
使用科大讯飞的时候公司已经有人配置好了Android项目,由于装机量的限制,所以基本上要更换里面的资源和更改app_id,然而在unity那边调用的时候总是出现各种各样的问题,特此记录下来 1.唤醒未 ...
- mysql中的左连接右连接内连接
一. 初始化SQL语句 /*join 建表语句*/ drop database if exists test; create database test; use test; /* 左表t1*/ dr ...
- Ubuntu 18.04 编译 ijkplayer
Ubuntu 18.04 编译 ijkplayer 1. 配置安装源 https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu 2. 配置好安装源后,安装一些组 ...
- serialVersionUID 作用
一个类在序列化时,用来记录当前类的版本号.在反序列化时,首先获取当前类的版本号,不一致则报异常. 若版本号一致,但两个类不一致.写肯定全部写进去.但提取时 类若没有这个字段,则不提取.若提取类多个字段 ...
- Jedis操作Redis技巧详解
对于Redis的部署模式有两种,单机模式 和 集群模式.因此,本文的介绍也从这两个方面进行介绍.众所周知,Jedis是最著名的Redis java客户端操作类库,几乎支持所有的Redis操作.本文就是 ...
- Comparing Xamarin and Delphi XE5 to Xcode for Cross Platform Mobile App Development
Comparing Xamarin and Delphi XE5 to Xcode for Cross Platform Mobile App Development If you are consi ...
- Linux下安装SVN(Subversion)
一.安装直接运行命令用YUM安装: yum install subversion -y 二.创建版本库创建版本库用svnadmin create命令,大概语法是svnadmin create svn库 ...
- Java进阶知识点4:不可变对象与并发 - 从String说起
一.String的不可变特性 熟悉Java的朋友都知道,Java中的String有一个很特别的特性,就是你会发现无论你调用String的什么方法,均无法修改this对象的状态.当确实需要修改Strin ...
- plsql基本操作 复制表 导出表 导出表结构 及其导入
上一片中介绍了安装instantclient +plsql取代庞大客户端的安装,这里说下plsql的基本操作 plsql操作界面图: 1.复制表 语句:create table IGIS_COPY a ...
- 在oracle里,如何取得本周、本月、本季度、本年度的第一天和最后一天的时间
在oracle里,如何取得本周.本月.本季度.本年度的第一天和最后一天的时间 --本周 select trunc(sysdate,'d')+1 from dual; select trunc(sysd ...