Files
python-book/02.面向对象/04.封装与多态.md
2025-09-15 11:25:51 +08:00

333 lines
8.4 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 中的多态是面向对象编程的核心概念,它允许不同类型的对象对相同的消息(方法调用)做出不同的响应,从而使用统一的接口处理不同的对象
。这种机制显著提高了代码的灵活性、可扩展性和可维护性。
## 封装
第一步:将内容封装到某处
```python
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
obj1 = Foo('张三',18)
obj2 = Foo('李四',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('张三',18)
obj2 = Foo('李四',16)
print(obj1.name)
print(obj2.age)
# 通过对象直接调用被封装的内容
obj1.detail()
obj2.detail()
# 通过self间接调用被封装的内容
```
## 多态
### 通过继承与方法重写实现多态
这是实现多态最直接的方式。子类继承父类并重写其方法,从而提供特定的实现。当调用方法时,实际执行的是子类中重写的方法
```python
class Animal:
def speak(self):
raise NotImplementedError("子类必须实现此方法") # 父类定义方法接口
class Dog(Animal):
def speak(self): # 子类重写方法
return "Woof!"
class Cat(Animal):
def speak(self): # 子类重写方法
return "Meow!"
# 多态的函数
def animal_speak(animal):
print(animal.speak()) # 统一接口调用
dog = Dog()
cat = Cat()
animal_speak(dog) # 输出: Woof!
animal_speak(cat) # 输出: Meow!
```
### 通过鸭子类型实现多态
Python 的鸭子类型Duck Typing是一种动态类型风格它不要求对象继承自特定类只关心对象是否具有所需的方法或属性“如果它走起来像鸭子叫起来像鸭子那么它就是鸭子”
```python
class Duck:
def quack(self):
return "Quack!"
class Person:
def quack(self): # 无需继承特定类,只需有 quack 方法
return "I'm quacking like a duck!"
def make_it_quack(thing): # 函数只关心传入对象是否有 quack 方法
print(thing.quack())
duck = Duck()
person = Person()
make_it_quack(duck) # 输出: Quack!
make_it_quack(person) # 输出: I'm quacking like a duck!
```
## 类的约束
写一个支付功能
```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()
```