python基础-面向对象的三大特征
2024-10-20 04:03:38
继承
单继承
父类 基类
子类 派生类
继承:是面向对象软件技术当中的一个概念,如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。
继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
字面意思就是:子承父业,合法继承家产,就是如果你是独生子,而且你也很孝顺,不出意外,你会继承你父母所有家产,他们的所有财产都会由你使用(败家子儿除外)。
对象查找属性的顺序
对象空间--类空间 --父类空间
对象查找方法的顺序
对象空间--类空间 --父类空间
类查找属性和方法顺序
类空间--父类空间
示例1
若子类和父类有同一属性名,优先执行子类的,若子类没有再去父类找,若想同时调用子类和父类的属性,可以通过 类名. 或者super()来实现
既用了父类的属性,也同时调用了自己的属性
class Person(Animal): live = '有些人活着,其实....' def __init__(self,name,sex,age,mind): # Animal.__init__(self, name,sex,age) ##利用类名调用属性 super().__init__(name,sex,age) #利用第super()来实现,就节省了self self.mind = mind def eat(self,): Animal.eat(self) print('人类也是需要吃饭的') class Cat(Animal): def __init__(self,name,sex,age,clim): super().__init__(name,sex,age) self.clim = clim class Dog(Animal): pass p1 = Person('大黄', '男', 17,'有思想') print(p1.__dict__) 打印结果: {'name': '大黄', 'sex': '男', 'age': 17, 'mind': '有思想'}
既用了父类的方法,也同时调用了自己的方法
class Animal: live = '活着' def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eat(self): print('动物在吃饭') class Person(Animal): live = '有些人活着,其实....' def eat(self): # Animal.eat(self) #直接使用类名来调用父类的方法 super().eat() ##利用继承关系来实现 print('人类也是需要吃饭的') class Cat(Animal): pass class Dog(Animal): pass p1 = Person('大黄','男',17) c1 = Cat('大橘','母',2) p1.eat() #当子类和父类方法名相同时 # 利用类名+方法的形式,可以在子类的方法中调用父类的方法,来实现既要用子类的功能,又要用父类的功能
多继承
子类不仅能有一个父类,还可以有多个父类,叫做多继承
它可以继承多个父类,并且可以使用所有父类的所有方法
class Shenxian: def fei(self): print('神仙会飞') class Monkey: def chitao(self): print('猴子喜欢吃桃子') class SunWuKong(Shenxian,Monkey): pass obj = SunWuKong() obj.chitao() obj.fei() 打印结果: 猴子喜欢吃桃子 神仙会飞
多继承中很好理解,但是在多继承中, 存在着这样⼀个问题. 当两个⽗类中出现了重名⽅法的时候. 这时该怎么办呢? 这时就涉及到如何查找⽗类⽅法的这么⼀个问题.即MRO(method resolution order) 问题. 在python中这是⼀个很复杂的问题. 因为在不同的python版本中使⽤的是不同的算法来完成MRO的.
这里需要补充一下python中类的种类(继承需要):
在python2x版本中存在两种类.:
⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object
经典类采用深度优先遍历方案
秉承着:从左至右,一条路走到头再返回的原则
新式类采用C3算法遍历原则,MRO序列
MRO就是一个有序列表
通用计算公式
mro(Child(Base1,Base2)) = [Child] + merge(mro(Base1),mro(Base2),[Base1,Base2])
其中:Child继承Base1和Base2
计算规则:
1.表头和表尾
表头:
列表的第一个元素是表头
表尾
列表中表头以外的元素集合就是表尾
2.列表之间的操作
[A] +[B] = [A,B]
如计算merge( [E,O], [C,E,F,O], [C] ) 有三个列表 : ① ② ③ 1 merge不为空,取出第一个列表列表①的表头E,进行判断 各个列表的表尾分别是[O], [E,F,O],E在这些表尾的集合中,因而跳过当前当前列表 2 取出列表②的表头C,进行判断 C不在各个列表的集合中,因而将C拿出到merge外,并从所有表头删除 merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] ) 3 进行下一次新的merge操作 ......
拿这个图来举例
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]
super()的真正含义
class A: def func1(self): print('in A func1') class B(A): def func1(self): super().func1() print('in B func1') class C(A): def func1(self): print('in C func1') class D(B,C): def func1(self): super().func1() #跳过本类,按照MRO的顺序执行下一类 print('in D func1') obj = D() obj.func1() #D类的MRO顺序是:[D,B,C,A] print(D.mro()) #查看该类的执行顺序 打印结果: in C func1 in B func1 in D func1
多态
同⼀个对象, 多种形态. 这个在python中其实是很不容易说明⽩的. 因为我们⼀直在⽤. 只是没有具体的说. 比如. 我们创建⼀个变量a = 10 , 我们知道此时a是整数类型. 但是我们可以通过程序让a = "alex", 这时, a⼜变成了字符串类型. 这是我们都知道的. 但是, 我要告诉你的是. 这个就是多态性. 同⼀个变量a可以是多种形态。
封装
函数 模块 类 对象都属于封装
把很多数据封装到⼀个对象中. 把固定功能的代码封装到⼀个代码块, 函数, 对象, 打包成模块. 这都属于封装的思想. 具体的情况具体分析. 比如. 你写了⼀个很⽜B的函数. 那这个也可以被称为封装. 在⾯向对象思想中. 是把⼀些看似⽆关紧要的内容组合到⼀起统⼀进⾏存储和使⽤. 这就是封装.
鸭子类型
看着像鸭子,就是鸭子
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 等等 这就是互称为鸭子类型。
类的约束
工作中如何对类进行约束,第一种方法,利用调用父类时的pay,就主动报错,raise,Python最常用的方式 class Payment: def pay(self, money): raise Exception('子类需要定义pay方法') class Alipay(Payment): def pay(self, money): print('您用阿里支付了%s元' % money) class QQpay(Payment): def pay(self, money): print('您用QQ支付了%s元' % money) class Wechatpay(Payment): def zhifu(self, money): print('您用微信支付了%s元' % money) obj = Wechatpay() obj.pay(100) 打印结果: obj.pay(100) File "C:/Users/15471/PycharmProjects/newpro/day22/exercise.py", line 45, in pay [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] raise Exception('子类需要定义pay方法') Exception: 子类需要定义pay方法
# 方法二 抽象类 接口类 : 制定一个规范,强制子类必须有pay方法,如果没有,在你实例化的时候就会报错 from abc import ABCMeta,abstractmethod class Payment(metaclass= ABCMeta): @abstractmethod def pay(self, money): pass class Alipay(Payment): def pay(self, money): print('您用阿里支付了%s元' % money) class QQpay(Payment): def pay(self, money): print('您用QQ支付了%s元' % money) class Wechatpay(Payment): def zhifu(self, money): print('您用微信支付了%s元' % money) obj = Wechatpay() obj.pay(100) 打印结果: 报错
所以此时我们要用到对类的约束,对类的约束有两种:
1. 提取⽗类. 然后在⽗类中定义好⽅法. 在这个⽅法中什么都不⽤⼲. 就抛⼀个异常就可以了. 这样所有的⼦类都必须重写这个⽅法. 否则. 访问的时候就会报错.
2. 使⽤元类来描述⽗类. 在元类中给出⼀个抽象⽅法. 这样⼦类就不得不给出抽象⽅法的具体实现. 也可以起到约束的效果.
最新文章
- 教你一招:win 7 或win 10右键菜单 添加获取管理员权限
- JSBinding / FAQ &; Trouble Shooting
- 关于STM32的FLASH操作【转载】
- 每天一个linux命令(14):head 命令
- Linux - Screen
- Socket,TCP/IP,UDP,HTTP,FTP
- Oracle之ORA-00972: identifier is too long
- python and java
- TreeView节点拖拉操作1
- 关键字 extern
- (转)AspNetPager查询分页问题(点击页码,不再是查询后的数据集)viewstate解决
- 如何使用SecureCRT连接ubuntu
- FineUIMvc随笔(4)自定义回发参数与自定义回发
- 微信小程序 登录流程规范解读
- 在Ubuntu下如何切换到超级用户
- 国际空间站直播 ISS直播
- Django学习笔记之视图高级-HTTP请求与响应
- foreach 引用传值&;
- ASP.NET MVC 富文本Ueditor编辑 后台传值前端乱码解决方案
- C语言写的trim()函数
热门文章
- left join \ right join \ inner join 详解
- ios中 input 焦点光标不垂直居中
- CoreDataDemo
- 最全的Spring注解详解
- Spring的七种事务传播机制
- 关于原生javascript的this,this真是个强大的东东
- canvas背景效果
- [PCL]对‘pcl::visualization::CloudViewer::CloudViewer(std::string const&;)’未定义的引用
- SPOJ BALNUM Balanced Numbers 平衡数(数位DP,状压)
- LibreOJ #514. 「LibreOJ β Round #2」模拟只会猜题意