Files
python-book/02.面向对象/04.封装与多态.md
2025-08-27 14:39:37 +08:00

314 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 封装与多态
python面向对象的三大特性继承封装多态。
1. **封装**: 把很多数据封装到⼀个对象中. 把固定功能的代码封装到⼀个代码块, 函数, 对象, 打包成模块. 这都属于封装的思想. 具体的情况具体分析. 比如. 你写了⼀个很⽜B的函数. 那这个也可以被称为封装. 在⾯向对象思想中. 是把⼀些看似⽆关紧要的内容组合到⼀起统⼀进⾏存储和使⽤. 这就是封装.
2. **继承**: ⼦类可以⾃动拥有⽗类中除了私有属性外的其他所有内容. 说⽩了, ⼉⼦可以随便⽤爹的东⻄. 但是朋友们, ⼀定要认清楚⼀个事情. 必须先有爹, 后有⼉⼦. 顺序不能乱, 在python中实现继承非常简单. 在声明类的时候, 在类名后⾯添加⼀个⼩括号,就可以完成继承关系. 那么什么情况可以使⽤继承呢? 单纯的从代码层⾯上来看. 两个类具有相同的功能或者特征的时候. 可以采⽤继承的形式. 提取⼀个⽗类, 这个⽗类中编写着两个类相同的部分. 然后两个类分别取继承这个类就可以了. 这样写的好处是我们可以避免写很多重复的功能和代码. 如果从语义中去分析的话. 会简单很多. 如果语境中出现了x是⼀种y. 这时, y是⼀种泛化的概念. x比y更加具体. 那这时x就是y的⼦类. 比如. 猫是⼀种动物. 猫继承动物. 动物能动. 猫也能动. 这时猫在创建的时候就有了动物的"动"这个属性. 再比如, ⽩骨精是⼀个妖怪. 妖怪天⽣就有⼀个比较不好的功能叫"吃⼈", ⽩骨精⼀出⽣就知道如何"吃⼈". 此时 ⽩骨精继承妖精.
3. **多态**: 同⼀个对象, 多种形态. 这个在python中其实是很不容易说明⽩的. 因为我们⼀直在⽤. 只是没有具体的说. 比如. 我们创建⼀个变量a = 10 , 我们知道此时a是整数类型. 但是我们可以通过程序让a = "hello", 这时, a⼜变成了字符串类型. 这是我们都知道的. 但是, 我要告诉你的是. 这个就是多态性. 同⼀个变量a可以是多种形态。
## 封装
第一步:将内容封装到某处
```python
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
obj1 = Foo('chensong',18)
obj2 = Foo('aaron',16)
```
第二步:从某处调用被封装的内容
```python
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def detail(self):
print(self.name)
print(self.age)
obj1 = Foo('chensong',18)
obj2 = Foo('aaron',16)
print(obj1.name)
print(obj2.age)
# 通过对象直接调用被封装的内容
obj1.detail()
obj2.detail()
# 通过self间接调用被封装的内容
```
## 多态
多态同一个对象多种形态。python默认支持多态。
python中有一句谚语说的好你看起来像鸭子那么你就是鸭子。
对于代码上的解释其实很简答:
```python
class A:
def f1(self):
print('in A f1')
def f2(self):
print('in A f2')
class B:
def f1(self):
print('in B f1')
def f2(self):
print('in B f2')
obj = A()
obj.f1()
obj.f2()
obj2 = B()
obj2.f1()
obj2.f2()
# A 和 B两个类完全没有耦合性但是在某种意义上他们却统一了一个标准。
# 对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互成为鸭子类型。
# 这样的例子比比皆是str tuple list 都有 index方法这就是统一了规范。
# str bytes 等等 这就是互称为鸭子类型。
```
## 类的约束
写一个支付功能
```python
class QQpay:
def pay(self,money):
print('使用qq支付%s元' % money)
class Alipay:
def pay(self,money):
print('使用阿里支付%s元' % money)
a = Alipay()
a.pay(100)
b = QQpay()
b.pay(200)
```
统一一下付款方式
```python
class QQpay:
def pay(self,money):
print('使用qq支付%s元' % money)
class Alipay:
def pay(self,money):
print('使用阿里支付%s元' % money)
def pay(obj,money):
obj.pay(money)
a = Alipay()
b = QQpay()
pay(a,100)
pay(b,200)
```
如果后期添加微信支付,但是没有统一标准,换个程序员就可能写成这样
```python
class QQpay:
def pay(self,money):
print('使用qq支付%s元' % money)
class Alipay:
def pay(self,money):
print('使用阿里支付%s元' % money)
class Wechatpay:
def fuqian(self,money):
print('使用微信支付%s元' % money)
def pay(obj,money):
obj.pay(money)
a = Alipay()
b = QQpay()
pay(a,100)
pay(b,200)
c = Wechatpay()
c.fuqian(300)
```
所以此时我们要用到对类的约束,对类的约束有两种:
1. 提取⽗类. 然后在⽗类中定义好⽅法. 在这个⽅法中什么都不⽤⼲. 就抛⼀个异常就可以了. 这样所有的⼦类都必须重写这个⽅法. 否则. 访问的时候就会报错.
2. 使⽤元类来描述⽗类. 在元类中给出⼀个抽象⽅法. 这样⼦类就不得不给出抽象⽅法的具体实现. 也可以起到约束的效果.
- 先用第一种方法解决问题
```python
class Payment:
"""
此类什么都不做,就是制定一个标准,谁继承我,必须定义我里面的方法。
"""
def pay(self,money):
raise Exception("你没有实现pay方法")
class QQpay(Payment):
def pay(self,money):
print('使用qq支付%s元' % money)
class Alipay(Payment):
def pay(self,money):
print('使用阿里支付%s元' % money)
class Wechatpay(Payment):
def fuqian(self,money):
print('使用微信支付%s元' % money)
def pay(obj,money):
obj.pay(money)
a = Alipay()
b = QQpay()
c = Wechatpay()
pay(a,100)
pay(b,200)
pay(c,300)
```
- 引入抽象类的概念处理
```python
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta): # 抽象类 接口类 规范和约束 metaclass指定的是一个元类
@abstractmethod
def pay(self):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 pay(self,money):
# print('使用微信支付了%s元'%money)
def recharge(self):pass
def pay(a,money):
a.pay(money)
a = Alipay()
a.pay(100)
pay(a,100) # 归一化设计:不管是哪一个类的对象,都调用同一个函数去完成相似的功能
q = QQpay()
q.pay(100)
pay(q,100)
w = Wechatpay()
pay(w,100) # 到用的时候才会报错
# 抽象类和接口类做的事情 :建立规范
# 制定一个类的metaclass是ABCMeta
# 那么这个类就变成了一个抽象类(接口类)
# 这个类的主要功能就是建立一个规范
```
总结: 约束. 其实就是⽗类对⼦类进⾏约束. ⼦类必须要写xxx⽅法. 在python中约束的⽅式和⽅法有两种:
1. 使⽤抽象类和抽象⽅法, 由于该⽅案来源是java和c#. 所以使⽤频率还是很少的
2. 使⽤⼈为抛出异常的⽅案. 并且尽量抛出的是NotImplementError. 这样比较专业, ⽽且错误比较明确.(推荐)
## super()深入了解
**super是严格按照类的继承顺序执行**
```python
class A:
def f1(self):
print('in A f1')
def f2(self):
print('in A f2')
class Foo(A):
def f1(self):
super().f2()
print('in A Foo')
obj = Foo()
obj.f1()
```
```python
class A:
def f1(self):
print('in A')
class Foo(A):
def f1(self):
super().f1()
print('in Foo')
class Bar(A):
def f1(self):
print('in Bar')
class Info(Foo,Bar):
def f1(self):
super().f1()
print('in Info f1')
obj = Info()
obj.f1()
print(Info.mro())
```
```python
class A:
def f1(self):
print('in A')
class Foo(A):
def f1(self):
super().f1()
print('in Foo')
class Bar(A):
def f1(self):
print('in Bar')
class Info(Foo,Bar):
def f1(self):
super(Foo,self).f1()
print('in Info f1')
obj = Info()
obj.f1()
```