# 封装与多态

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()
```

