# 面对对象

2023年8月03日17分钟阅读

- FormalSciences/ComputerScience
- ProgrammingLanguage/Python
- Python/Basics

面向对象编程（Object-Oriented Programming，OOP）是一种编程范式或模型，以「对象」作为核心来设计和实现软件。这种方法主要的目标是将数据和处理数据的函数结合在一起，封装成独立的软件模块，我们称之为「对象」。面向对象编程被广泛应用于多种编程语言中，包括 Python。

## 基本概念

### 理念和用途

**面向对象编程是在更高的抽象层次上思考问题的一种方式。**面向对象编程的主要理念是，将现实世界中的对象抽象化，将对象的属性（也叫状态）和行为（也叫方法）封装在一起。面向对象编程的思考方式，更贴近我们对现实世界的认知。

面向对象编程的主要优点包括：

1. 代码复用性：类的定义一旦完成，就可以在任何地方创建其对象，复用类的属性和方法，而无需重复编写代码。通过类之间的继承关系，子类可以继承父类的代码，从而实现代码的复用。
2. 代码的可维护性和可扩展性：面向对象编程的封装特性，保护了对象内部的数据，使得代码更安全，更易于维护。通过类的继承和多态，可以轻松实现对现有代码的扩展。

### 特点

面向对象编程有三个主要的特点：封装、继承、和多态。

- 封装：封装是指将对象的状态（数据）和行为（方法）包装在一起，隐藏对象的内部实现细节，仅对外提供必要的接口。这样可以保护对象的内部状态，提高代码的安全性，也提高了代码的可维护性。
- 继承：继承是指子类可以继承父类的属性和方法。继承让我们可以在父类的基础上进行扩展，实现代码的复用，提高代码的可扩展性。
- 多态：多态是指不同类的对象对同一消息会做出不同的响应。多态提供了统一的接口，使得我们可以使用一致的方式处理不同类的对象，提高了代码的灵活性。

## Python 中的类和对象

在 Python 中，我们使用 `class` 关键字来定义类。类是一种数据类型，它定义了一种新的对象，这种对象具有自己的属性和方法。

### 类和对象的概念

- 类 (Class)： 类是对象的蓝图或原型。你可以想象成是创建对象的模板，它定义了特定类型的对象的属性和方法。

    # 一个简单的 Python 类的例子
    class Dog:
        pass

- 对象 (Object)：对象是类的实例。具体来说，当我们根据类的定义创建了一个实例后，这个实例就是一个对象。每个对象都具有类定义的属性和方法。

    # 创建Dog类的一个实例
    my_dog = Dog()

我们可以通过创建类的实例，也就是对象，来使用类的属性和方法。

### 类的属性和方法

类的属性和方法是类的主要组成部分。属性用于描述类和其实例的状态，方法用于描述类和其实例可以进行的操作。

在 Python 中，我们可以在类中定义两种类型的属性和方法：

- 实例属性和方法：实例属性和方法属于类的实例。每个实例都有自己的实例属性，这些属性与其他实例的属性互不影响。实例方法可以通过实例来调用，它可以访问和修改实例的属性。

    # 在Python类中定义实例属性和方法的例子
    class Dog:
        def __init__(self, name):
            self.name = name  # 实例属性

        def bark(self):  # 实例方法
            return f"{self.name} says woof!"

- 类属性和方法：类属性和方法是属于类本身的，所有的实例都会共享同一个类属性。类方法可以通过类名直接调用，也可以通过实例调用，但是它不能访问和修改实例属性。

    # 在Python类中定义类属性和方法的例子
    class Dog:
        species = "Canis familiaris"  # 类属性

        @classmethod
        def description(cls):  # 类方法
            return "This class represents a dog."

### `__init__`

在 Python 中使用 `__init__` 方法来初始化类的实例。这个方法会在创建实例时自动调用，我们可以在这个方法中设置实例的初始状态，也就是定义实例的属性。

`__init__` 方法的第一个参数始终是 `self`，代表了实例本身。在 `__init__` 方法中，我们使用 `self.属性名` 来定义实例属性。

    class Dog:
        def __init__(self, name, age):
            self.name = name
            self.age = age

## 封装

### 概念和用途

封装，这是面向对象编程（OOP）中的基本概念之一，其目标是通过将数据（对象的状态）和对象的行为组合在一起，来隐藏或封装对象内部的详细信息。简单地说，封装就是把客户端代码（对象的使用者）从对象的内部细节中解耦出来。

封装的主要优势包括：

1. 提高了代码的安全性：封装可以防止对象状态的无意义或不合适的修改。只有通过定义好的方法（有时被称为 getter 和 setter ）才能修改状态，这些方法可以控制对对象状态的更改，从而保证其正确和一致。
2. 提高了代码的可维护性：由于客户端代码从对象的内部实现中解耦出来，我们可以自由地改变对象的内部实现，而不会影响到使用该对象的代码。

### Python 中实现封装

#### 数据封装

在 Python 中，我们可以通过使用私有属性（私有变量）实现数据封装。**Python 中的私有属性名字前面带有两个下划线（例如 `__name`）。**这些属性只能在类的内部被访问，不能在类的外部直接访问，从而实现了数据的封装。

    class Dog:
        def __init__(self, name, age):
            # __name和__age是私有属性，我们不能在类的外部直接访问
            self.__name = name
            self.__age = age

#### 方法封装

Python 同样可以通过私有方法实现方法的封装。私有方法的名字也以两个下划线开始，它们只能在类的内部被调用，不能在类的外部直接调用。

    class Dog:
        def __init__(self, name, age):
            self.__name = name
            self.__age = age

        # __bark 是一个私有方法，我们不能在类的外部直接调用
        def __bark(self):
            return f"{self.__name} says woof!"

尽管私有属性和方法不能在类的外部直接访问，但是我们可以提供公共的 getter 和 setter 方法，让外部代码能够以受控的方式读写这些私有属性。这样我们就可以保证封装的安全性，同时也提供了一定的灵活性。

    class Dog:
        def __init__(self, name, age):
            self.__name = name
            self.__age = age

        # getter for __name
        def get_name(self):
            return self.__name

        # setter for __name
        def set_name(self, name):
            self.__name = name

        # getter for __age
        def get_age(self):
            return self.__age

        # setter for __age
        def set_age(self, age):
            self.__age = age

        # __bark 是一个私有方法，我们不能在类的外部直接调用
        def __bark(self):
            return f"{self.__name} says woof!"

        # 公共方法可以调用私有方法
        def public_bark(self):
            return self.__bark()

## 继承

### 继承的概念和用途

继承是面向对象编程中的核心概念之一。继承允许我们定义一个新的类（子类或派生类）来继承现有类（父类或基类）的属性（变量）和方法（函数）。这样我们就可以在新类中复用父类的代码，避免了代码的重复，同时也可以在新类中添加新的方法或对父类的方法进行重写（override）。

继承的主要优点包括：

1. 代码复用：子类继承了父类的所有属性和方法，所以我们可以通过创建子类，来复用和扩展父类的代码，减少了代码的重复。
2. 易于维护和修改：由于子类可以重写父类的方法，这就意味着我们可以在不改变父类的情况下，改变子类的行为。这使得代码更容易维护和修改。

### Python 中实现继承

#### 单继承

在 Python 中，我们可以通过在类定义时的括号中写入父类的名字来创建子类。新定义的类将会继承父类的所有属性和方法。

    class Animal:
        def __init__(self, name):
            self.name = name

    # Dog 是 Animal 的子类，它继承了 Animal 的所有属性和方法。
    class Dog(Animal):
        pass

#### 多继承

Python 支持多继承，意味着一个类可以继承多个父类。但这可能会导致一些复杂性，如“菱形问题”。

    class Swimming:
        def swim(self):
            return "Swims!"

    class Flying:
        def fly(self):
            return "Flies!"

    class Duck(Swimming, Flying):
        pass

    duck = Duck()
    print(duck.swim())  # Outputs: Swims!
    print(duck.fly())   # Outputs: Flies!

#### `super` 函数

在 Python 中，`super` 函数是一个内置函数，它可以用来调用父类的方法。这在你需要在子类中扩展父类的方法时特别有用。

    class Animal:
        def __init__(self, name):
            self.name = name

    # Dog 的 __init__ 方法中使用了 super().__init__(name) 来调用 Animal 的 __init__ 方法。
    class Dog(Animal):
        def __init__(self, name, breed):
            super().__init__(name)  # 调用父类的构造方法
            self.breed = breed  # 新增的属性

## 多态

### 多态的概念和用途

在面向对象编程（OOP）中，多态性是一种允许一个实体采取多种形态的能力。更具体地说，多态是指通过同一个接口，使用不同的实例，可以产生不同的结果。这使得我们可以在运行时确定我们正在使用的对象类型，然后执行相应的操作。

多态的优势主要有两点：

1. 提高代码的灵活性：多态能够让我们以更一般的方式编写代码，处理更广泛的数据类型，而不仅仅是特定的单一数据类型。这使得我们的代码更加灵活和可维护。
2. 提高代码的可扩展性：如果我们想要添加新的数据类型，我们只需要确保它们遵循现有的接口规定。这使得我们的代码更易于扩展和改进，而无需修改大量现有代码。
3. 促进了类之间的解耦：多态允许系统在不修改现有代码的情况下，添加新的对象类型。

### Python 中实现多态

Python 是一种动态类型语言，意味着我们不需要明确地声明对象的类型。这就让我们在 Python 中能够非常容易地实现多态。以下是 Python 中实现多态的一些方法：

#### 方法重写

在 Python 中，子类可以重写父类的方法。这就意味着，当我们在子类中调用一个父类的方法时，将会执行子类中定义的版本，而非父类中的原始版本。这使得我们可以改变子类中方法的行为，以符合我们的需求。

    class Animal:
        def sound(self):
            return "Generic animal sound"

    # Dog类重写了Animal类的sound方法，所以当我们调用Dog实例的sound方法时，会返回"Woof!"，而不是"Generic animal sound"。
    class Dog(Animal):
        def sound(self):
            return "Woof!"

#### Duck Typing

Python 支持一种编程概念叫做 duck typing，这也是实现多态的一种方法。Duck typing 的核心思想是：如果一个对象能够像鸭子一样走路，像鸭子一样叫，那么我们就可以认为它是鸭子。对于 Python 来说，如果一个对象有我们需要的方法，我们就可以使用它，而不管它是什么类型的对象（运行时类型确定）。

    def animal_sound(animal):
        # 这个函数只关心对象是否有sound方法，而不关心对象的具体类型
        return animal.sound()

    print(animal_sound(Dog()))  # 输出"Woof!"
    print(animal_sound(Animal()))  # 输出"Generic animal sound"

## 抽象类和接口

### 抽象类和接口的概念

抽象类是一种特殊的类，它不能被实例化，只能被继承。抽象类中可以定义抽象方法，这些方法在抽象类中没有实现，在子类中必须实现。

接口是一种特殊的抽象类，它只定义了一组方法的签名，没有提供任何实现。接口定义了一组行为，任何实现了这些行为的类都可以说是实现了这个接口。

### Python 的 Abc 模块

在 Python 中，我们使用 abc 模块来创建抽象类和接口。abc 模块提供了 `ABC` 基类和 `abstractmethod` 装饰器，我们可以使用它们来定义抽象类和抽象方法。

### Python 中使用抽象类和接口

#### 创建抽象类

我们可以使用 `ABC` 基类和 `abstractmethod` 装饰器来创建抽象类。

    from abc import ABC, abstractmethod

    # AbstractAnimal 是一个抽象类，它有一个抽象方法 sound。
    class AbstractAnimal(ABC):
        @abstractmethod
        def sound(self):
            pass

#### 创建接口

在 Python 中，接口的概念可以通过完全由抽象方法构成的抽象类来实现。与抽象类类似，我们使用 ABC 基类和 abstractmethod 装饰器来定义接口。

    from abc import ABC, abstractmethod

    # AnimalBehaviour 是一个接口，它定义了两个抽象方法：eat 和 sleep。
    class AnimalBehaviour(ABC):
        @abstractmethod
        def eat(self):
            pass

        @abstractmethod
        def sleep(self):
            pass

#### 实现接口

我们可以通过继承抽象类并实现其所有的抽象方法来实现接口。

    class Dog(AbstractAnimal, AnimalBehaviour):
        def sound(self):
            return "Woof!"

        def eat(self):
            return "The dog is eating."

        def sleep(self):
            return "The dog is sleeping."