327 lines
8.1 KiB
Markdown
327 lines
8.1 KiB
Markdown
# 封装与多态
|
||
|
||
python面向对象的三大特性:继承,封装,多态。
|
||
|
||
- **封装**:
|
||
- 封装是将**数据(属性)**与**操作数据的方法(行为)**绑定在一起,形成独立的类,并通过访问控制隐藏对象的内部实现细节,仅暴露必要的接口供外部使用。其核心目的是**保护数据完整性**(防止外部直接修改对象状态)和**降低代码耦合度**(外部无需关心内部实现)。
|
||
|
||
- **继承**:
|
||
- 继承是**子类(派生类)**继承**父类(基类/超类)**的属性和方法的机制,用于实现代码复用和类的层次结构。子类可以扩展父类的功能(添加新属性/方法),或重写父类的方法(修改默认行为)。
|
||
|
||
- **多态**:
|
||
- 多态是指**同一操作**作用于**不同对象**时,产生**不同行为**的特性。其核心是“接口统一,实现多样”,允许程序以统一的方式处理不同类型的对象,无需关心对象的具体类型。
|
||
|
||
## 封装
|
||
|
||
Python中实现封装的方式包括:
|
||
|
||
- **私有属性/方法**:
|
||
- 通过双下划线前缀(如`__name`)标记
|
||
- 此类属性/方法仅在类内部可访问,外部无法直接调用(其实就是改名,`_类名__name` );
|
||
- **公有方法**:
|
||
- 通过普通方法(如`get_name()`、`set_name()`)提供对私有属性的访问接口,
|
||
- 可在方法内加入数据验证(如确保年龄为正数);
|
||
- **`@property`装饰器**:
|
||
- 将方法转换为“只读属性”(如`@property def radius(self)`),或通过`@属性.setter`设置属性的修改规则(如`@radius.setter def radius(self, value)`)
|
||
- 实现更安全的属性访问。
|
||
|
||
```python
|
||
class BankAccount:
|
||
def __init__(self, balance):
|
||
self.__balance = balance # 私有属性,隐藏内部数据
|
||
|
||
def deposit(self, amount):
|
||
if amount > 0:
|
||
self.__balance += amount # 通过公有方法修改私有属性
|
||
else:
|
||
raise ValueError("存款金额必须为正")
|
||
|
||
def get_balance(self):
|
||
return self.__balance # 通过公有方法访问私有属性
|
||
```
|
||
|
||
封装避免了外部代码直接操作对象内部数据,提升了代码的安全性和可维护性
|
||
|
||
## 多态
|
||
|
||
### 通过继承与方法重写实现多态
|
||
|
||
这是实现多态最直接的方式。子类继承父类并重写其方法,从而提供特定的实现。当调用方法时,实际执行的是子类中重写的方法
|
||
|
||
```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() # 关键:跳过Foo,从Bar开始查找f1
|
||
print('in Info f1')
|
||
|
||
obj = Info()
|
||
obj.f1()
|
||
```
|
||
|