【Python入门】一篇就能让你完全掌握Python中的类和对象

PS:本篇博客超级详细,阅读时请尽量使用电脑端结合目录

导读

在编程中,类和对象是面向对象编程(OOP)的核心概念。Python 是一门支持面向对象编程的语言,这意味着你可以使用类和对象来组织代码,使其更加模块化、可维护和可扩展。

1. 什么是类(Class)

  • 类是一个蓝图或模板,用来描述某一类对象的共同特征和行为。它定义了对象应该具有什么属性(变量)和行为(方法)。
  • 举个例子,如果你要创建一个汽车管理系统,你可能会定义一个 Car 类,其中包括汽车的品牌、颜色、速度等属性,以及启动、停止、加速等行为。

类的定义

在 Python 中,类使用 class 关键字来定义。让我们来看一个简单的例子:

class Car:
    # 类的属性和方法定义在这里
    pass
  • class Car: 是类的定义,其中 Car 是类的名称。
  • pass 是一个占位符,表示目前类中没有任何内容,但这是一个合法的类定义。

2. 什么是对象(Object)

  • 对象是根据类创建的具体实例。你可以把类看作是一个模具,而对象则是根据这个模具制作出来的具体产品。
  • 如果 Car 是一个类,那么你可以创建多个对象来代表不同的汽车,如 my_car,your_car 等。

创建对象

要创建一个对象,只需要调用类本身,就像调用一个函数一样:

my_car = Car()
  • my_car 是 Car 类的一个实例或对象。
  • 现在,my_car 这个对象可以拥有 Car 类中定义的所有属性和行为。

3. 属性和方法

属性(Attributes)
  • 属性是对象的状态或特征。它们是存储在对象中的变量。
  • 例如,汽车的品牌、颜色、速度都可以作为属性来存储在 Car 对象中。
方法(Methods)
  • 方法是对象可以执行的操作或行为。它们是定义在类中的函数。
  • 例如,汽车可以启动、停止、加速,这些行为可以通过方法来实现。

在类中定义属性和方法

让我们扩展 Car 类,添加一些属性和方法:

class Car:
    # 定义属性
    def __init__(self, brand, color):
        self.brand = brand  # 汽车品牌
        self.color = color  # 汽车颜色

    # 定义方法
    def start(self):
        print(f"The {self.color} {self.brand} car is starting.")

    def stop(self):
        print(f"The {self.color} {self.brand} car is stopping.")
'
  • init 方法:这是一个特殊的方法,当你创建对象时,Python 会自动调用它。这个方法用于初始化对象的属性。self 是对象自身的引用,后面会详细解释。
  • start 和 stop 方法:这些是普通的方法,用于描述汽车的行为。

使用对象的属性和方法

现在我们可以创建一个 Car 对象,并使用它的属性和方法:

my_car = Car("Toyota", "Red")
print(my_car.brand)  # 输出: Toyota
print(my_car.color)  # 输出: Red

my_car.start()  # 输出: The Red Toyota car is starting.
my_car.stop()   # 输出: The Red Toyota car is stopping.
  • 当我们创建 my_car 对象时,init 方法被自动调用,brand 被设置为 “Toyota”,color 被设置为 “Red”。
  • 我们可以通过 my_car.brand 和 my_car.color 访问对象的属性,也可以调用 start() 和 stop() 方法来执行相应的行为。

4. self 参数的作用

  • 在方法定义中,self 参数用于指代当前对象的实例。在方法内部,通过 self 可以访问对象的属性和其他方法。
  • 当调用方法时,Python 会自动将调用该方法的对象传递给 self。因此,在定义方法时,self 必须作为第一个参数。

举个例子:

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color

    def start(self):
        print(f"The {self.color} {self.brand} car is starting.")
'

当你调用 my_car.start() 时,实际上等同于 Car.start(my_car),即 Python 会自动将 my_car 对象传递给 self。

5. 小结

类: 一个蓝图,定义了一类对象的属性和行为。
对象: 根据类创建的具体实例,具有类定义的属性和方法。
属性: 对象的特征或状态,存储在对象中的变量。
方法: 对象的行为或操作,定义在类中的函数。
self 参数: 引用当前对象,用于访问对象的属性和方法。

两个问题

Python 类中的缩进

在 Python 中,缩进是代码块的唯一表示方式,这与 C++ 和许多其他语言不同。在 Python 中,没有使用 {} 来定义代码块,而是通过缩进来明确代码块的层级关系。

  • 类的缩进:

a.在类定义的最外层,所有方法和属性的定义都需要有相同的缩进级别。
b.类中的每个方法或函数,其内容需要进一步缩进,以表示它们属于该方法。

例如:

class Car:
    def __init__(self, brand, color):
        self.brand = brand  # 这行代码相对于方法的定义缩进了一个层级
        self.color = color

    def start(self):
        print(f"The {self.color} {self.brand} car is starting.")

在这个示例中:

  • def __init__(self, brand, color): 是类的一个方法,它相对于 class Car: 缩进了四个空格(或一个 tab)。
  • self.brand = brandself.color = color__init__ 方法中的代码,它们相对于 def __init__ 缩进了四个空格。
2.为什么 Python 没有像 C++ 那样单独定义成员变量

在 Python 中,没有像 C++ 那样在类的开头单独定义成员变量(属性)的部分。这是因为 Python 采用了一种更灵活的方式来处理对象的属性。这种设计有几个重要原因:

2.1 动态语言的特性

  • 动态类型

Python 是一种动态类型的语言,这意味着变量的类型在运行时决定,并且你可以在程序的任何地方为对象添加新的属性。
这与 C++ 等静态类型语言不同。在 C++ 中,你必须在编译时定义对象的所有成员变量(属性)的类型和数量。

2.2 __init__ 方法的灵活性

  • 属性在 __init__ 方法中定义:

    • 在 Python 中,通常通过 __init__ 方法来初始化对象的属性。__init__ 方法是一个构造函数,当你创建一个对象时,这个方法会被自动调用。
    • __init__ 方法中使用 self 关键字定义的属性,实际上就是成员变量。这种方式可以根据初始化时传入的参数动态地定义和设置对象的属性。

例如:

class Car:
    def __init__(self, brand, color):
        self.brand = brand  # 这里 brand 就是一个成员变量
        self.color = color  # color 也是一个成员变量

在这个例子中,brand 和 color 是 Car 对象的成员变量,通过 self.brand 和 self.color 在 init 方法中定义。

2.3 灵活性和可扩展性
  • 动态添加属性:

Python 的对象可以在程序的运行时动态地添加或修改属性。这为程序设计提供了极大的灵活性。

例如:

my_car = Car("Toyota", "Red")
my_car.year = 2020  # 动态添加一个新的属性 year

在运行时,你可以为 my_car 对象添加一个新的属性 year,这是在 C++ 等静态类型语言中难以实现的。

  • 更少的冗余:

由于 Python 的灵活性,你不需要在类的定义中事先声明所有可能的属性,而是可以根据实际需要在 init 方法或其他方法中定义它们。

6. 实例化多个对象

在前面的例子中,我们已经学会了如何创建一个对象,并使用它的属性和方法。现在我们将学习如何创建多个对象,并理解对象之间的独立性。

当你实例化多个对象时,每个对象都是类的一个独立实例,拥有自己独立的属性值。这意味着你可以创建多个对象,它们可以拥有不同的状态(属性值),但都可以共享相同的行为(方法)。

class Car:
    def __init__(self, brand, color):
        self.brand = brand  # 每个对象都有独立的 brand 属性
        self.color = color  # 每个对象都有独立的 color 属性

    def start(self):
        print(f"The {self.color} {self.brand} car is starting.")

    def stop(self):
        print(f"The {self.color} {self.brand} car is stopping.")

# 创建多个对象
car1 = Car("Toyota", "Red")
car2 = Car("Honda", "Blue")
car3 = Car("Ford", "Black")

# 使用这些对象
car1.start()  # 输出: The Red Toyota car is starting.
car2.start()  # 输出: The Blue Honda car is starting.
car3.start()  # 输出: The Black Ford car is starting.

代码解读:

  • 独立的对象:car1, car2, car3 是 Car 类的三个独立实例。每个对象都有自己的 brand 和 color 属性。
  • 方法的共享:虽然每个对象的属性值不同,但它们都可以调用 start() 和 stop() 方法,这些方法在所有对象之间是共享的。

通过实例化多个对象,你可以清晰地理解对象之间的独立性和类的灵活性。

7. 添加更多方法

我们已经学习了如何定义基本的方法,现在我们将进一步扩展我们的类,添加更多的方法以模拟更加复杂的行为。

为类添加新的方法

你可以为类添加任意数量的方法,这些方法可以操作对象的属性或执行特定的任务。让我们继续扩展 Car 类,添加一个 accelerate 方法,用来模拟汽车的加速。

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color
        self.speed = 0  # 初始速度为0

    def start(self):
        self.speed = 10  # 启动后设置初始速度
        print(f"The {self.color} {self.brand} car is starting at {self.speed} km/h.")

    def accelerate(self, increase):
        self.speed += increase
        print(f"The {self.color} {self.brand} car is accelerating to {self.speed} km/h.")

    def stop(self):
        self.speed = 0  # 停车时速度为0
        print(f"The {self.color} {self.brand} car is stopping.")

# 使用新的方法
car1 = Car("Toyota", "Red")
car1.start()         # 输出: The Red Toyota car is starting at 10 km/h.
car1.accelerate(30)  # 输出: The Red Toyota car is accelerating to 40 km/h.
car1.stop()          # 输出: The Red Toyota car is stopping.

代码解读:

  • speed 属性:我们添加了一个 speed 属性来跟踪汽车的速度。
  • accelerate 方法:这个方法用于增加汽车的速度。它接收一个 increase 参数,用来指定要增加的速度值。

8.类的继承

继承是面向对象编程的一个重要特性,它允许你通过创建一个新的类,复用已有类的属性和方法。继承使得代码更加模块化、可重用和易于维护。

什么是继承?

当一个类(子类)继承另一个类(父类)时,子类自动拥有父类的所有属性和方法。你可以在子类中添加新的属性和方法,或者重写父类的方法。

让我们定义一个 ElectricCar 类,它继承自 Car类,并添加一些特有的功能。

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color
        self.speed = 0

    def start(self):
        self.speed = 10
        print(f"The {self.color} {self.brand} car is starting at {self.speed} km/h.")

    def accelerate(self, increase):
        self.speed += increase
        print(f"The {self.color} {self.brand} car is accelerating to {self.speed} km/h.")

    def stop(self):
        self.speed = 0
        print(f"The {self.color} {self.brand} car is stopping.")

# 定义一个 ElectricCar 类,继承自 Car 类
class ElectricCar(Car):
    def __init__(self, brand, color, battery_capacity):
        super().__init__(brand, color)  # 调用父类的构造函数
        self.battery_capacity = battery_capacity  # 电池容量

    def charge(self):
        print(f"The {self.color} {self.brand} electric car is charging with {self.battery_capacity} kWh capacity.")

# 使用 ElectricCar 类
electric_car = ElectricCar("Tesla", "White", 85)
electric_car.start()  # 调用继承的 start 方法
electric_car.accelerate(50)  # 调用继承的 accelerate 方法
electric_car.charge()  # 调用 ElectricCar 类中特有的方法
electric_car.stop()  # 调用继承的 stop 方法

代码解读:

  • super() 函数:在 ElectricCar 类中,我们使用 super() 函数调用了父类的 __init__ 方法,以便在子类中初始化父类的属性。
  • 子类扩展:ElectricCar 类扩展了 Car 类,添加了一个新的属性 battery_capacity,以及一个新的方法 charge

9. 方法重写(Method Overriding)

方法重写是在子类中定义与父类中同名的方法,以覆盖或扩展父类的行为。通过方法重写,子类可以改变或定制从父类继承的方法的功能。

9.1 为什么需要方法重写?

在继承中,子类会继承父类的所有方法和属性。然而,有时你可能希望子类的某些方法与父类的方法表现不同。这时,你就可以在子类中重写这些方法。

9.2 方法重写的基本示例

我们来定义一个基本的 Car 类,然后在子类 ElectricCar 中重写它的方法。

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color
        self.speed = 0

    def start(self):
        self.speed = 10
        print(f"The {self.color} {self.brand} car is starting at {self.speed} km/h.")

    def stop(self):
        self.speed = 0
        print(f"The {self.color} {self.brand} car is stopping.")

# 定义一个 ElectricCar 类,继承自 Car 类
class ElectricCar(Car):
    def __init__(self, brand, color, battery_capacity):
        super().__init__(brand, color)  # 调用父类的构造函数初始化 brand 和 color
        self.battery_capacity = battery_capacity

    def start(self):
        # 重写 start 方法,让电动汽车以不同的方式启动
        self.speed = 20  # 电动汽车启动时速度更快
        print(f"The {self.color} {self.brand} electric car is silently starting at {self.speed} km/h with {self.battery_capacity} kWh capacity.")

    def charge(self):
        print(f"The {self.color} {self.brand} electric car is charging with {self.battery_capacity} kWh capacity.")

# 创建 ElectricCar 对象并使用它
electric_car = ElectricCar("Tesla", "White", 85)
electric_car.start()  # 输出: The White Tesla electric car is silently starting at 20 km/h with 85 kWh capacity.
electric_car.stop()   # 输出: The White Tesla car is stopping.
electric_car.charge()  # 输出: The White Tesla electric car is charging with 85 kWh capacity.

9.3 代码详解

  • 父类 Car 的方法:Car 类定义了 start 和 stop 方法。start 方法设置初始速度为 10 km/h,stop 方法将速度设为 0。
  • 子类 ElectricCar 的方法重写:
    • ElectricCar 继承了 Car 的所有属性和方法。
    • ElectricCar 重写了 start 方法,使其启动速度为 20 km/h,并增加了电池容量的输出。
    • super().__init__(brand, color) 调用了父类的构造函数,以确保 brand 和 color 属性被正确初始化。

10.多继承(Multiple Inheritance)

多继承是指一个类可以同时继承多个父类的特性。Python 支持多继承,但使用时需要注意可能的复杂性,尤其是在多个父类中有相同方法的情况下。

10.1 多继承的概念

多继承允许一个子类同时从多个父类中继承方法和属性。这种特性非常强大,但也可能带来复杂的依赖关系。

10.2 多继承的示例

创建两个父类 Vehicle 和 Electric,然后定义一个子类 ElectricCar,它同时继承自这两个父类。

class Vehicle:
    def __init__(self, brand):
        self.brand = brand

    def start(self):
        print(f"The {self.brand} vehicle is starting.")

class Electric:
    def __init__(self, battery_capacity):
        self.battery_capacity = battery_capacity

    def charge(self):
        print(f"Charging with {self.battery_capacity} kWh battery capacity.")

# ElectricCar 继承了 Vehicle 和 Electric
class ElectricCar(Vehicle, Electric):
    def __init__(self, brand, color, battery_capacity):
        Vehicle.__init__(self, brand)  # 初始化 Vehicle 类的属性
        Electric.__init__(self, battery_capacity)  # 初始化 Electric 类的属性
        self.color = color

    def start(self):
        print(f"The {self.color} {self.brand} electric car is silently starting with {self.battery_capacity} kWh battery.")

# 使用 ElectricCar 类
electric_car = ElectricCar("Tesla", "White", 85)
electric_car.start()   # 输出: The White Tesla electric car is silently starting with 85 kWh battery.
electric_car.charge()  # 输出: Charging with 85 kWh battery capacity.

10.3 代码详解

  • Vehicle 和 Electric 父类:这两个类分别提供 brand 和 battery_capacity 属性,并各自定义了 start 和 charge 方法。
  • ElectricCar 的多继承:
    • ElectricCar 同时继承了 Vehicle 和 Electric 的属性和方法。
    • 通过在 __init__ 方法中分别调用 Vehicle.__init__(self, brand) Electric.__init__(self, battery_capacity) 来初始化父类的属性。
    • ElectricCar 中的 start 方法重写了 Vehicle 类的 start方法,以自定义启动行为。

10.4 方法解析顺序(MRO)

当使用多继承时,Python 会根据方法解析顺序(MRO)来决定调用哪个父类的方法。MRO 是 Python 确定方法调用顺序的规则。

  • 查看 MRO:

    • 你可以通过 ClassName.mro() 方法查看一个类的 MRO。

例如:

print(ElectricCar.mro())

这将输出:

[<class '__main__.ElectricCar'>, <class '__main__.Vehicle'>, <class '__main__.Electric'>, <class 'object'>]
  • MRO 解析顺序: Python 会从左到右、从上到下地查找方法。在本例中,ElectricCar 先查找自身,然后是 Vehicle,接着是 Electric,最后是 Python 所有类的基类 object。

11. 类的组合(Composition)

类的组合是指一个类可以包含其他类的实例作为属性,从而创建更复杂的对象。与继承相比,组合更灵活,因为它不涉及继承链的复杂性。

11.1 什么是类的组合?

类的组合通过在一个类中包含另一个类的实例,来实现功能模块化。这种方法使得类的职责更加明确,也让代码更加易于维护。

11.2 类的组合的示例

让我们定义一个 Battery 类,并将其组合到 ElectricCar 中。

class Battery:
    def __init__(self, capacity):
        self.capacity = capacity

    def charge(self):
        print(f"Charging the battery with {self.capacity} kWh capacity.")

class ElectricCar:
    def __init__(self, brand, color, battery_capacity):
        self.brand = brand
        self.color = color
        self.battery = Battery(battery_capacity)  # 组合 Battery 类

    def start(self):
        print(f"The {self.color} {self.brand} electric car is starting.")

    def charge(self):
        self.battery.charge()  # 调用 Battery 类的 charge 方法

# 使用 ElectricCar 类
electric_car = ElectricCar("Tesla", "White", 85)
electric_car.start()  # 输出: The White Tesla electric car is starting.
electric_car.charge()  # 输出: Charging the battery with 85 kWh capacity.
11.3 代码详解
  • Battery 类:Battery 类代表电动汽车的电池。它有一个 charge 方法来模拟充电。
  • ElectricCar 的组合:
    • ElectricCar 类拥有一个 Battery 类的实例,作为其 battery 属性。
    • 当调用 electric_car.charge() 时,实际是调用 Battery 类的 charge 方法。

11.4 组合与继承的对比

  • 继承: 用于创建一个“是一个”关系的类结构,例如 ElectricCar 是一个 Car。
  • 组合: 用于创建一个“有一个”关系的类结构,例如 ElectricCar 有一个 Battery。

组合通常比继承更灵活,因为你可以在不改变类继承层次的情况下,动态地更改组合类的行为。

12.小结

方法重写: 通过在子类中定义与父类同名的方法,你可以重写父类的方法,从而定制子类的行为。重写方法时,子类的方法会覆盖父类的同名方法,这是实现多态性的重要方式。
多继承: Python 支持一个类同时继承多个父类的属性和方法。多继承虽然强大,但使用时需要注意方法解析顺序(MRO)和潜在的复杂性。通过多继承,你可以复用多个类的功能,从而创建功能更加丰富的子类。
类的组合: 通过在一个类中包含其他类的实例,你可以创建复杂的对象结构。这种方法比多继承更加灵活且易于维护,因为它不涉及复杂的继承关系。组合强调对象之间的协作,而不是继承层次中的耦合。

13.方法解析顺序(MRO)

在多继承中,方法解析顺序(MRO)是 Python 用来确定类层次结构中的方法调用顺序的机制。理解 MRO 对于有效使用多继承和调试复杂的类层次结构非常重要。

13.1 MRO 的基本概念

MRO 决定了当你调用一个方法时,Python 如何查找该方法的定义。MRO 是通过一种称为 C3 线性化的算法来计算的,该算法确保了类层次结构中的一致性和方法解析的确定性。

13.2 查看 MRO

你可以使用类的 mro() 方法或 __mro__ 属性来查看一个类的 MRO。

例如:

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

print(D.mro())

输出将显示 D 类的 MRO:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

13.3 复杂继承结构中的 MRO

当类层次结构变得复杂时,MRO 可以帮助你理解方法调用的顺序。例如,如果 D 类继承了 B 和 C,而 B 和 C 又都继承自 A,MRO 可以告诉你调用 D 的方法时,Python 是如何在 B、C 和 A 之间选择方法的。

通过了解 MRO,你可以避免潜在的方法冲突,并确保你的代码在多继承情况下能够按照预期工作。

14. 抽象类和接口

在面向对象编程中,抽象类和接口用于定义类的框架和通用行为,确保子类实现这些行为。

14.1 什么是抽象类?

抽象类是一种不能被实例化的类,它通常用于定义子类必须实现的方法。抽象类提供了一个模板,让你可以确保所有子类都具有某些共同的行为。

14.2 Python 中的抽象类

在 Python 中,你可以使用 abc 模块中的 ABC 和 abstractmethod 来定义抽象类和抽象方法。

例如:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

class Dog(Animal):
    def sound(self):
        return "Woof!"

class Cat(Animal):
    def sound(self):
        return "Meow!"

在这个例子中,Animal 是一个抽象类,它定义了一个抽象方法 sound。任何继承 Animal 的子类都必须实现 sound 方法,否则会引发错误。

14.3 抽象类的用途

  • 统一接口: 抽象类强制子类实现某些方法,从而确保所有子类有统一的接口。
  • 代码复用: 抽象类可以包含具体的方法实现,这些方法可以被子类继承和复用。
  • 增强代码的可维护性: 通过抽象类,你可以更容易地理解和维护子类之间的关系和行为

15. 接口和协议(Interfaces and Protocols)

15.1 什么是接口?

接口是一个类必须遵循的规则或约定,它定义了类应该具备哪些方法,但不提供具体的实现。在 Python 中,接口常用在规定某些类必须实现特定的方法。通过接口,不同的类可以被相同的代码调用,只要它们实现了接口的要求。

15.2 协议的基本概念

协议是 Python 中的一种接口定义方式,常用于规定一个类应该具备哪些方法。协议是“非正式”的接口,它不要求显式地继承任何东西,只需要类实现了协议中的方法。

例子:定义飞行协议

from typing import Protocol

# 定义一个飞行协议,规定类必须有 fly 方法
class Flyer(Protocol):
    def fly(self) -> None:
        pass

# 定义一个 Bird 类,它实现了 fly 方法
class Bird:
    def fly(self) -> None:  # 注意 -> None 表示这个方法不返回任何值
        print("Bird is flying")

# 定义一个 Airplane 类,它也实现了 fly 方法
class Airplane:
    def fly(self) -> None:
        print("Airplane is flying")

# 定义一个函数,这个函数接受任何有 fly 方法的对象
def make_fly(flyer: Flyer):
    flyer.fly()

# 创建 Bird 和 Airplane 的实例,并传递给 make_fly 函数
bird = Bird()
plane = Airplane()

make_fly(bird)    # 输出: Bird is flying
make_fly(plane)   # 输出: Airplane is flying

详细解释

1.Flyer(Protocol): Flyer 是一个协议类,它定义了所有实现此协议的类必须具备的 fly 方法。
2.fly(self) -> None: -> None 的意思是这个方法不返回任何值。None 是 Python 的一种特殊类型,表示什么都没有。
3.make_fly(flyer: Flyer): 这个函数接受任何实现 Flyer 协议的对象作为参数。无论是 Bird 还是 Airplane,只要它们实现了 fly 方法,就可以传给这个函数。

输出示例

Bird is flying
Airplane is flying

这个示例展示了 Bird 和 Airplane 类如何实现同样的 fly 方法,使得它们都可以被 make_fly 函数调用。

16. 装饰器模式(Decorator Pattern)

16.1 什么是装饰器?

装饰器是 Python 中的一个强大特性,允许你在不修改原始函数的情况下,为函数添加额外的功能。装饰器本质上是一个函数,它接收另一个函数作为参数,并返回一个新的函数。

例子:简单的函数装饰器

# 定义一个简单的装饰器函数
def simple_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()  # 调用原始函数
        print("Something is happening after the function is called.")
    return wrapper

# 使用装饰器
@simple_decorator  # @符号表示我们使用simple_decorator来装饰say_hello函数
def say_hello():
    print("Hello!")

# 调用被装饰的函数
say_hello()

详细解释

1.@simple_decorator: 这个语法糖(简便写法)表示将 say_hello 函数传递给 simple_decorator 装饰器,相当于 say_hello = simple_decorator(say_hello)。
2.wrapper(): 装饰器内部定义的 wrapper 函数包裹了原始的 say_hello 函数,在调用 say_hello 时,会先执行 wrapper 内的代码。
3.func(): func 是原始的 say_hello 函数,通过调用它,我们在 wrapper 中执行了原始函数的功能。

输出示例

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

16.2 为什么 @ 符号放在函数定义上面?

  • @符号的作用: 它是用来简化装饰器应用的。如果没有 @ 符号,你需要手动将函数传给装饰器。使用 @ 符号时,装饰器会在函数定义之后立即应用,不需要手动传递。

代码对比

# 使用 @ 语法糖
@simple_decorator
def say_hello():
    print("Hello!")

# 不使用 @ 语法糖,等同于上面的代码
def say_hello():
    print("Hello!")
say_hello = simple_decorator(say_hello)

17. 上下文管理器(Context Managers)

17.1 什么是上下文管理器?

上下文管理器用于在一段代码运行前后自动管理资源,比如文件、网络连接等。上下文管理器确保资源在使用后被正确释放,避免资源泄漏问题。

示例:使用上下文管理器管理文件

# 使用上下文管理器打开文件
with open('example.txt', 'w') as file:
    file.write('Hello, World!')

# 这个文件会在 with 语句块结束时自动关闭

17.2 自定义上下文管理器

你可以通过定义 __enter____exit__ 方法来自定义上下文管理器。这两个方法分别在上下文管理器的进入和退出时执行。

示例:自定义上下文管理器

class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Exiting the context")
        if exc_type:
            print(f"Exception type: {exc_type}")
            print(f"Exception value: {exc_val}")
            print(f"Traceback: {exc_tb}")
        return True  # 处理异常后,继续执行

# 使用自定义上下文管理器
with MyContextManager():
    print("Inside the context")
    raise ValueError("Oops!")  # 故意引发一个异常

详细解释

1.enter: 当你进入 with 语句时,__enter__ 方法会被自动调用。你可以在这里做一些初始化操作。
2.exit: 当 with 语句结束时,__exit__ 方法会被调用。这个方法接受三个参数:exc_type(异常类型)、exc_val(异常值)、exc_tb(异常的追踪信息)。这些参数用于处理在 with 语句块内发生的任何异常。
3.return True: 如果 __exit__ 返回 True,异常将被抑制,不会向外抛出。如果返回 False 或者不返回,异常将会被继续抛出。

输出示例

Entering the context
Inside the context
Exiting the context
Exception type: <class 'ValueError'>
Exception value: Oops!
Traceback: <traceback object at 0x...>

18. 元类(Metaclasses)

18.1 什么是元类?

元类是用来创建类的“类”。普通类是用来创建对象的,而元类是用来创建类的。元类通常用于自动修改类的定义或行为。

示例:简单的元类

class MyMeta(type):
    def __new__(cls, name, bases, dct):
        print(f"Creating class {name}")
        return super().__new__(cls, name, bases, dct)

class MyClass(metaclass=MyMeta):
    pass

# 创建 MyClass 的实例
obj = MyClass()

详细解释

1.type: type 是 Python 内置的元类,用于创建所有的类。MyMeta 继承自 type,我们通过 __new__ 方法来控制类的创建。
2.new(cls, name, bases, dct): __new__ 是一个特殊的方法,它在 __init__ 之前被调用,用于创建类。cls 是元类本身,name 是类的名称,bases 是类的基类(父类),dct 是类的属性和方法的字典。
3.metaclass=MyMeta: 当你定义 MyClass 时,Python 会使用 MyMeta 元类来创建 MyClass 类。

输出示例

Creating class MyClass

当你定义 MyClass 时,会输出 Creating class MyClass,表示元类 MyMeta 正在创建 MyClass。

19. 面向对象设计原则(SOLID Principles)

19.1 SOLID 原则简介

SOLID 是面向对象设计的五大原则,用来编写可维护、可扩展的代码。我们一条一条来看。

19.1.1 单一职责原则(SRP)

单一职责原则(Single Responsibility Principle)要求一个类应该只负责一件事。这样可以让类更简单、更易维护。

示例:单一职责原则

class ReportGenerator:
    def generate(self):
        print("Generating report")

class ReportSaver:
    def save(self):
        print("Saving report")
  • ReportGenerator 负责生成报告。
  • ReportSaver 负责保存报告。每个类都有明确的职责,不会混在一起。

19.1.2 开放/封闭原则(OCP)

开放/封闭原则(Open/Closed Principle)要求软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。

示例:开放/封闭原则

class Shape:
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * (self.radius ** 2)

class Rectangle(Shape):
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width
  • 通过继承 Shape 类,我们可以创建新的形状类,而不需要修改现有代码。

19.1.3 里氏替换原则(LSP)

里氏替换原则(Liskov Substitution Principle)要求子类对象必须能够替换基类对象,而不会影响程序的正确性。

示例:里氏替换原则

class Bird:
    def fly(self):
        print("Bird is flying")

class Penguin(Bird):
    def fly(self):
        raise NotImplementedError("Penguins cannot fly")

# 这种设计违反了里氏替换原则,因为企鹅不能飞,但它继承了能飞的鸟类
  • Penguin 不能替代 Bird,因为企鹅不会飞,这违反了里氏替换原则。

19.1.4 接口隔离原则(ISP)

接口隔离原则(Interface Segregation Principle)要求类不应该被强迫实现它不需要的接口或方法。

示例:接口隔离原则

class Workable:
    def work(self):
        pass

class Eatable:
    def eat(self):
        pass

class Worker(Workable, Eatable):
    def work(self):
        print("Working")

    def eat(self):
        print("Eating")

class Robot(Workable):
    def work(self):
        print("Working")

# Robot 只实现了它需要的接口,而不是所有接口
  • Robot 只实现了 Workable 接口,而不需要实现 Eatable 接口。

19.1.5 依赖倒置原则(DIP)

依赖倒置原则(Dependency Inversion Principle)要求高层模块不应该依赖于低层模块,而应该依赖于抽象(接口)。

示例:依赖倒置原则

class Keyboard:
    def type(self):
        return "Typing"

class Monitor:
    def display(self):
        return "Displaying"

class Computer:
    def __init__(self, keyboard: Keyboard, monitor: Monitor):
        self.keyboard = keyboard
        self.monitor = monitor

    def operate(self):
        print(self.keyboard.type())
        print(self.monitor.display())

Computer 依赖于 Keyboard 和 Monitor 抽象,而不是它们的具体实现,这使得更换这些组件变得容易。

一起交流讨论吧

学习不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我的学习圈子一起解决自学python难题、面试难题、以及工作上的问题。

加入方式:私我~

好了今天分享就到此,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一 键三连哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值