【Python篇】Python 类和对象:详细讲解(下篇)

2024-10-09 19:32:10 浏览数 (1)

Python 类和对象:详细讲解(下篇)

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

15.1 什么是接口?

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

15.2 协议的基本概念

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

例子:定义飞行协议
代码语言:javascript复制
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 方法,就可以传给这个函数。
输出示例
代码语言:javascript复制
Bird is flying
Airplane is flying

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


16. 装饰器模式(Decorator Pattern)

16.1 什么是装饰器?

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

例子:简单的函数装饰器
代码语言:javascript复制
# 定义一个简单的装饰器函数
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 中执行了原始函数的功能。
输出示例
代码语言:javascript复制
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
16.2 为什么 @ 符号放在函数定义上面?
  • @符号的作用: 它是用来简化装饰器应用的。如果没有 @ 符号,你需要手动将函数传给装饰器。使用 @ 符号时,装饰器会在函数定义之后立即应用,不需要手动传递。
代码对比
代码语言:javascript复制
# 使用 @ 语法糖
@simple_decorator
def say_hello():
    print("Hello!")

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

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

17.1 什么是上下文管理器?

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

示例:使用上下文管理器管理文件
代码语言:javascript复制
# 使用上下文管理器打开文件
with open('example.txt', 'w') as file:
    file.write('Hello, World!')

# 这个文件会在 with 语句块结束时自动关闭
17.2 自定义上下文管理器

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

示例:自定义上下文管理器
代码语言:javascript复制
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 或者不返回,异常将会被继续抛出。
输出示例
代码语言:javascript复制
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 什么是元类?

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

示例:简单的元类
代码语言:javascript复制
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 类。
输出示例
代码语言:javascript复制
Creating class MyClass

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


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

19.1 SOLID 原则简介

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

19.1.1 单一职责原则(SRP)

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

示例:单一职责原则
代码语言:javascript复制
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)要求软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。

示例:开放/封闭原则
代码语言:javascript复制
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)要求子类对象必须能够替换基类对象,而不会影响程序的正确性。

示例:里氏替换原则
代码语言:javascript复制
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)要求类不应该被强迫实现它不需要的接口或方法。

示例:接口隔离原则
代码语言:javascript复制
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)要求高层模块不应该依赖于低层模块,而应该依赖于抽象(接口)。

示例:依赖倒置原则
代码语言:javascript复制
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 依赖于 KeyboardMonitor 抽象,而不是它们的具体实现,这使得更换这些组件变得容易。

以上就是关于【Python篇】Python 类和对象:详细讲解(下篇)的内容啦,各位大佬有什么问题欢迎在评论区指正,您的支持是我创作的最大动力!❤️

0 人点赞