Python魔法之旅-魔法方法(04)

news2024/11/17 5:23:35

目录

一、概述

1、定义

2、作用

二、主要应用场景

1、构造和析构

2、操作符重载

3、字符串和表示

4、容器管理

5、可调用对象

6、上下文管理

7、属性访问和描述符

8、迭代器和生成器

9、数值类型

10、复制和序列化

11、自定义元类行为

12、自定义类行为

13、类型检查和转换

14、自定义异常

三、学习方法

1、理解基础

2、查阅文档

3、编写示例

4、实践应用

5、阅读他人代码

6、参加社区讨论

7、持续学习

8、练习与总结

9、注意兼容性

10、避免过度使用

四、魔法方法

14、__enter__方法

14-1、语法

14-2、参数

14-3、功能

14-4、返回值

14-5、说明

14-6、用法

15、__eq__方法

15-1、语法

15-2、参数

15-3、功能

15-4、返回值

15-5、说明

15-6、用法

16、__exit__方法

16-1、语法

16-2、参数

16-3、功能

16-4、返回值

16-5、说明

16-6、用法

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页

一、概述

1、定义

        魔法方法(Magic Methods/Special Methods,也称特殊方法或双下划线方法)是Python中一类具有特殊命名规则的方法,它们的名称通常以双下划线(`__`)开头和结尾

        魔法方法用于在特定情况下自动被Python解释器调用,而不需要显式地调用它们,它们提供了一种机制,让你可以定义自定义类时具有与内置类型相似的行为。

2、作用

        魔法方法允许开发者重载Python中的一些内置操作或函数的行为,从而为自定义的类添加特殊的功能

二、主要应用场景

1、构造和析构

1-1、__init__(self, [args...]):在创建对象时初始化属性。
1-2、__new__(cls, [args...]):在创建对象时控制实例的创建过程(通常与元类一起使用)。
1-3、__del__(self):在对象被销毁前执行清理操作,如关闭文件或释放资源。

2、操作符重载

2-1、__add__(self, other)、__sub__(self, other)、__mul__(self, other)等:自定义对象之间的算术运算。
2-2、__eq__(self, other)、__ne__(self, other)、__lt__(self, other)等:定义对象之间的比较操作。

3、字符串和表示

3-1、__str__(self):定义对象的字符串表示,常用于print()函数。
3-2、__repr__(self):定义对象的官方字符串表示,用于repr()函数和交互式解释器。

4、容器管理

4-1、__getitem__(self, key)、__setitem__(self, key, value)、__delitem__(self, key):用于实现类似列表或字典的索引访问、设置和删除操作。
4-2、__len__(self):返回对象的长度或元素个数。

5、可调用对象

5-1、__call__(self, [args...]):允许对象像函数一样被调用。

6、上下文管理

6-1、__enter__(self)、__exit__(self, exc_type, exc_val, exc_tb):用于实现上下文管理器,如with语句中的对象。

7、属性访问和描述符

7-1、__getattr__, __setattr__, __delattr__:这些方法允许对象在访问或修改不存在的属性时执行自定义操作。
7-2、描述符(Descriptors)是实现了__get__, __set__, 和__delete__方法的对象,它们可以控制对另一个对象属性的访问。

8、迭代器和生成器

8-1、__iter__和__next__:这些方法允许对象支持迭代操作,如使用for循环遍历对象。
8-2、__aiter__, __anext__:这些是异步迭代器的魔法方法,用于支持异步迭代。

9、数值类型

9-1、__int__(self)、__float__(self)、__complex__(self):定义对象到数值类型的转换。
9-2、__index__(self):定义对象用于切片时的整数转换。

10、复制和序列化

10-1、__copy__和__deepcopy__:允许对象支持浅复制和深复制操作。
10-2、__getstate__和__setstate__:用于自定义对象的序列化和反序列化过程。

11、自定义元类行为

11-1、__metaclass__(Python 2)或元类本身(Python 3):允许自定义类的创建过程,如动态创建类、修改类的定义等。

12、自定义类行为

12-1、__init__和__new__:用于初始化对象或控制对象的创建过程。
12-2、__init_subclass__:在子类被创建时调用,允许在子类中执行一些额外的操作。

13、类型检查和转换

13-1、__instancecheck__和__subclasscheck__:用于自定义isinstance()和issubclass()函数的行为。

14、自定义异常

14-1、你可以通过继承内置的Exception类来创建自定义的异常类,并定义其特定的行为。

三、学习方法

        要学好Python的魔法方法,你可以遵循以下方法及步骤:

1、理解基础

        首先确保你对Python的基本语法、数据类型、类和对象等概念有深入的理解,这些是理解魔法方法的基础。

2、查阅文档

        仔细阅读Python官方文档中关于魔法方法的部分,文档会详细解释每个魔法方法的作用、参数和返回值。你可以通过访问Python的官方网站或使用help()函数在Python解释器中查看文档。

3、编写示例

        为每个魔法方法编写简单的示例代码,以便更好地理解其用法和效果,通过实际编写和运行代码,你可以更直观地感受到魔法方法如何改变对象的行为。

4、实践应用

        在实际项目中尝试使用魔法方法。如,你可以创建一个自定义的集合类,使用__getitem__、__setitem__和__delitem__方法来实现索引操作。只有通过实践应用,你才能更深入地理解魔法方法的用途和重要性。

5、阅读他人代码

        阅读开源项目或他人编写的代码,特别是那些使用了魔法方法的代码,这可以帮助你学习如何在实际项目中使用魔法方法。通过分析他人代码中的魔法方法使用方式,你可以学习到一些新的技巧和最佳实践。

6、参加社区讨论

        参与Python社区的讨论,与其他开发者交流关于魔法方法的使用经验和技巧,在社区中提问或回答关于魔法方法的问题,这可以帮助你更深入地理解魔法方法并发现新的应用场景。

7、持续学习

        Python语言和其生态系统不断发展,新的魔法方法和功能可能会不断被引入,保持对Python社区的关注,及时学习新的魔法方法和最佳实践。

8、练习与总结

        多做练习,通过编写各种使用魔法方法的代码来巩固你的理解,定期总结你学到的知识和经验,形成自己的知识体系。

9、注意兼容性

        在使用魔法方法时,要注意不同Python版本之间的兼容性差异,确保你的代码在不同版本的Python中都能正常工作。

10、避免过度使用

        虽然魔法方法非常强大,但过度使用可能会导致代码难以理解和维护,在编写代码时,要权衡使用魔法方法的利弊,避免滥用。

        总之,学好Python的魔法方法需要不断地学习、实践和总结,只有通过不断地练习和积累经验,你才能更好地掌握这些强大的工具,并在实际项目中灵活运用它们。

四、魔法方法

14、__enter__方法

14-1、语法
__enter__(self)
    return self # 可以返回任何对象,或者简单地返回self
14-2、参数

14-2-1、self(必须)一个对实例对象本身的引用,在类的所有方法中都会自动传递。

14-3、功能

        用于在进入一个由with语句控制的代码块时执行特定的操作。

14-4、返回值

        在with语句块开始执行时调用,并通常返回一个对象。

14-5、说明

        在Python的官方文档中,__enter__ 方法是不接受任何参数的。但是,如果你真的看到了带有参数的 __enter__ 方法,那可能是某个特定库或框架的扩展用法,或者可能是对该方法的误用。

14-6、用法
# 014、__enter__方法:
# 1、文件操作
# 定义一个名为 FileContextManager 的类,用于管理文件的上下文
class FileContextManager:
    # 初始化方法,接收一个文件名作为参数
    def __init__(self, filename):
        # 将传入的文件名保存到实例变量 filename 中
        self.filename = filename
    # 定义 __enter__ 方法,用于 with 语句执行时进入上下文时调用
    def __enter__(self):
        # 使用实例变量 filename 打开文件,并设置文件模式为只读 ('r')
        self.file = open(self.filename, 'r')
        # 返回打开的文件对象,以便在 with 语句的 as 子句中引用
        return self.file
    # 定义 __exit__ 方法,用于 with 语句执行完毕后退出上下文时调用
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 关闭之前打开的文件对象
        self.file.close()
 # 使用 with 语句和 FileContextManager 类来管理文件操作
# 'test.txt' 是要打开的文件名,'file' 是打开文件后在 with 语句内部使用的引用名
with FileContextManager('test.txt') as file:
    # 读取文件内容并打印,假设文件内容是 "Hello, World!"
    print(file.read())  # 输出:Hello, World!

# 2、数据库连接
# 假设有一个数据库连接库(但在这里我们只是模拟它)
class DatabaseContext:
    # 定义一个上下文管理器的进入方法
    def __enter__(self):
        # 当使用 with 语句进入上下文时,该方法会被调用
        # 这里只是一个示例,实际中会使用数据库连接库来建立真实的数据库连接
        print("Connecting to database...")
        # 假设我们有一个模拟的数据库连接字符串
        self.connection = "Mock Database Connection"
        # 返回连接对象(或连接字符串),以便在 with 语句的 as 子句中引用
        return self.connection
    # 定义一个上下文管理器的退出方法
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 当 with 语句块结束时,无论是否发生异常,该方法都会被调用
        print("Closing database connection...")
        # 在这里,我们只是打印了一条消息,但在真实情况下,你会关闭数据库连接
# 使用 with 语句和 DatabaseContext 类来管理数据库连接的上下文
with DatabaseContext() as db:
    # 在这里使用数据库连接(这里只是打印了一条消息)
    # 假设 db 是一个数据库连接对象,但在本例中它只是一个字符串
    print("Using database:", db)
    # 当 with 语句块结束时,__exit__ 方法会被自动调用,打印 "Closing database connection..."

# 3、临时目录
# 导入tempfile模块,用于创建临时文件和目录
import tempfile
# 导入shutil模块,用于删除目录及其内容
import shutil
# 定义一个名为TempDirContext的上下文管理类
class TempDirContext:
    # 定义__enter__方法,当使用with语句进入上下文时调用
    def __enter__(self):
        # 使用tempfile.mkdtemp()方法创建一个临时目录
        self.temp_dir = tempfile.mkdtemp()
        # 打印创建的临时目录的路径
        print(f"Created temp directory: {self.temp_dir}")
        # 返回临时目录的路径,以便在with语句的as子句中引用
        return self.temp_dir
     # 定义__exit__方法,当with语句块结束时调用
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 打印将要删除的临时目录的路径
        print(f"Deleting temp directory: {self.temp_dir}")
        # 使用shutil.rmtree()方法删除临时目录及其所有内容
        shutil.rmtree(self.temp_dir)
# 使用with语句和TempDirContext类来管理临时目录的上下文
with TempDirContext() as temp_dir:
    # 打印正在使用的临时目录的路径
    print(f"Using temp directory: {temp_dir}")
    # 在这里可以使用temp_dir变量来引用临时目录,并执行相关操作
    # 例如:在临时目录中创建文件、写入数据等
    # 注意:由于此代码片段是一个注释示例,所以没有包含实际的文件操作

# 4、锁机制
# 导入threading模块,用于多线程操作
import threading
# 定义一个名为ThreadLock的类,用于管理线程锁
class ThreadLock:
    # 初始化方法,创建一个threading.Lock对象
    def __init__(self):
        self.lock = threading.Lock()
    # 定义__enter__方法,当使用with语句进入上下文时调用
    def __enter__(self):
        # 尝试获取锁
        self.lock.acquire()
        # 打印锁已被获取的消息
        print("Acquired lock")
        # 返回锁对象(虽然通常不需要返回,但这里为了示例还是返回了)
        return self.lock
    # 定义__exit__方法,当with语句块结束时调用
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 释放锁
        self.lock.release()
        # 打印锁已被释放的消息
        print("Released lock")
# 使用with语句和ThreadLock类来管理线程锁
with ThreadLock() as lock:
    # 在这里进行线程安全的操作
    # 注意:因为with语句已经处理了锁的获取和释放,所以这里的lock变量其实不需要使用
    # 除非你想在with块内部检查锁的状态或做其他与锁相关的操作
    pass

# 5、设置和恢复环境变量
# 定义一个名为EnvVarContext的上下文管理器类,用于临时修改环境变量
class EnvVarContext:
    def __init__(self, var_name, new_value):
        # 初始化时接收环境变量名和新值
        self.var_name = var_name  # 存储要修改的环境变量名
        self.new_value = new_value  # 存储新的环境变量值
        self.old_value = None  # 用于存储原始的环境变量值
    def __enter__(self):
        # 当使用with语句进入上下文时调用
        # 获取环境变量原来的值
        self.old_value = os.environ.get(self.var_name)
        # 设置新的环境变量值
        os.environ[self.var_name] = self.new_value
        # 打印已设置新的环境变量值
        print(f"Set {self.var_name} to {self.new_value}")
        # 因为with语句的as子句没有使用变量来接收返回值,所以这里不需要返回具体的值
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 当with语句块结束时调用
        # 恢复环境变量为原来的值
        if self.old_value is not None:
            # 如果原值存在,则恢复为原值
            os.environ[self.var_name] = self.old_value
        else:
            # 如果原值不存在,则从环境变量中删除该变量
            del os.environ[self.var_name]
            # 打印环境变量已恢复为原始值或已删除
        print(f"Restored {self.var_name} to its original value")
# 导入os模块,以便操作环境变量
import os
# 使用with语句和EnvVarContext类来修改环境变量
with EnvVarContext('MY_VAR', 'new_value') as _:
    # 在with块内部,MY_VAR的值已被临时修改为'new_value'
    print(os.environ['MY_VAR'])  # 输出: new_value
# 退出with块后,MY_VAR被恢复为原来的值或删除
# 之后的代码可以确认MY_VAR的值是否已被恢复

# 6、模拟上下文状态管理
class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        # 在这里,你可以设置任何进入上下文时需要的状态或资源
        # 例如,打开一个文件、获取一个锁等
        self.resource = "some resource"  # 假设这是一个需要管理的资源
        return self  # 通常,__enter__ 应该返回一个对象,以便在 with 块中使用
    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")
        # 在这里,你可以执行任何清理工作,例如关闭文件、释放锁等
        # 你可以检查 exc_type, exc_value, traceback 来确定是否发生了异常
        # 如果发生了异常并且你想重新抛出它,则不应该在这里进行任何操作
        # 如果你想忽略异常,你可以在这里返回 True
        # 如果你想在退出时总是执行一些清理代码,无论是否发生异常,你可以将这里留空
# 使用 with 语句和上下文管理器
with MyContextManager() as manager:
    print("Inside the with block, accessing resource:", manager.resource)
    # 这里是代码块,当执行到这里时,__enter__ 已经被调用
    # 当这个代码块执行完毕(或者发生异常)时,__exit__ 将会被调用

15、__eq__方法

15-1、语法
__eq__(self, other, /)
    Return self==other
15-2、参数

15-2-1、self(必须)表示调用该方法的对象本身。

15-2-2、other(必须)表示与self进行相等性比较操作的对象。

15-2-3、/(可选)这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。

15-3、功能

        用于定义对象之间的相等性比较。

15-4、返回值

        返回一个布尔值(True或False),表示两个对象是否相等。

15-5、说明

        当你使用==运算符来比较两个对象是否相等时,Python会尝试调用这两个对象的__eq__方法(如果它们定义了的话)。

15-6、用法
# 015、__eq__方法:
# 1、简单的数值类
# 定义一个名为Number的类,用于封装数值
class Number:
    # 类的初始化方法,当创建Number类的实例时调用
    def __init__(self, value):
        # 给实例对象添加一个属性value,并赋值为传入的参数value
        self.value = value
    # 类的相等性比较方法,用于比较两个Number对象是否相等
    def __eq__(self, other):
        # 检查other是否是Number类的实例
        if isinstance(other, Number):
            # 如果是,则比较两个Number对象的value属性是否相等
            return self.value == other.value
            # 如果other不是Number类的实例,则直接返回False
        return False
# 当这个脚本作为主程序运行时,以下代码会被执行
if __name__ == '__main__':
    # 创建两个Number对象,它们的value属性都是5
    num1 = Number(5)
    num2 = Number(5)
    # 创建一个Number对象,它的value属性是10
    num3 = Number(10)
    # 使用==操作符比较num1和num2是否相等,并打印结果(注释说明预期输出为True)
    print(num1 == num2)  # 输出: True
    # 使用==操作符比较num1和num3是否相等,并打印结果(注释说明预期输出为False)
    print(num1 == num3)  # 输出: False

# 2、点类(二维坐标)
# 定义一个名为Point的类,用于表示二维平面上的点
class Point:
    # 初始化方法,当创建Point类的实例时调用
    def __init__(self, x, y):
        # 设置点的x坐标
        self.x = x
        # 设置点的y坐标
        self.y = y
    # 相等性比较方法,用于比较两个Point对象是否相等
    def __eq__(self, other):
        # 检查other是否也是Point类的实例
        if isinstance(other, Point):
            # 如果是,则比较两个点的x和y坐标是否都相等
            return self.x == other.x and self.y == other.y
            # 如果other不是Point类的实例,则返回False
        return False
# 当这个脚本作为主程序运行时,以下代码会被执行
if __name__ == '__main__':
    # 创建一个Point对象p1,其x坐标为1,y坐标为2
    p1 = Point(1, 2)
    # 创建一个Point对象p2,其x坐标为1,y坐标为2(与p1相同)
    p2 = Point(1, 2)
    # 创建一个Point对象p3,其x坐标为2,y坐标为2(与p1不同)
    p3 = Point(2, 2)
    # 使用==操作符比较p1和p2是否相等,并打印结果(注释说明预期输出为True)
    print(p1 == p2)  # 输出: True
    # 使用==操作符比较p1和p3是否相等,并打印结果(注释说明预期输出为False)
    print(p1 == p3)  # 输出: False

# 3、字符串封装类
# 定义一个名为StringWrapper的类,用于封装字符串  
class StringWrapper:
    # 初始化方法,当创建StringWrapper类的实例时调用  
    def __init__(self, text):
        # 给实例对象添加一个属性text,并赋值为传入的参数text  
        self.text = text
    # 相等性比较方法,用于比较两个StringWrapper对象是否相等  
    def __eq__(self, other):
        # 检查other是否也是StringWrapper类的实例  
        if isinstance(other, StringWrapper):
            # 如果是,则比较两个StringWrapper对象的text属性是否相等  
            return self.text == other.text
        # 如果other不是StringWrapper类的实例,则返回False  
        return False
# 当这个脚本作为主程序运行时,以下代码会被执行  
if __name__ == '__main__':
    # 创建一个StringWrapper对象s1,封装字符串"hello"  
    s1 = StringWrapper("hello")
    # 创建一个StringWrapper对象s2,封装字符串"hello"(与s1相同)  
    s2 = StringWrapper("hello")
    # 创建一个StringWrapper对象s3,封装字符串"world"(与s1不同)  
    s3 = StringWrapper("world")
    # 使用==操作符比较s1和s2是否相等,并打印结果(注释说明预期输出为True)  
    print(s1 == s2)  # 输出: True  
    # 使用==操作符比较s1和s3是否相等,并打印结果(注释说明预期输出为False)  
    print(s1 == s3)  # 输出: False

# 4、用户类(只比较用户名)
# 定义一个名为 User 的类,表示用户信息
class User:
    # 初始化方法,当创建 User 类的实例时会被调用
    def __init__(self, username, age):
        # 设置实例属性 username,存储用户名
        self.username = username
        # 设置实例属性 age,存储年龄
        self.age = age
    # 重写 __eq__ 方法,用于比较两个 User 实例是否相等
    # 这里的相等定义为:如果两个 User 实例的 username 属性相等,则它们相等
    def __eq__(self, other):
        # 检查 other 是否是 User 类的实例
        if isinstance(other, User):
            # 如果 other 是 User 类的实例,则比较两者的 username 属性
            return self.username == other.username
            # 如果 other 不是 User 类的实例,则返回 False
        return False
# 如果当前脚本作为主程序运行(而不是被导入为模块)
if __name__ == '__main__':
    # 创建一个 User 实例,用户名为 "Myelsa",年龄为 18
    user1 = User("Myelsa", 18)
    # 创建一个 User 实例,用户名为 "Myelsa",年龄为 28
    user2 = User("Myelsa", 28)
    # 创建一个 User 实例,用户名为 "Jimmy",年龄为 15
    user3 = User("Jimmy", 15)
    # 使用 __eq__ 方法比较 user1 和 user2 是否相等,并打印结果
    # 因为它们的 username 相等,所以输出为 True
    print(user1 == user2)  # 输出: True
    # 使用 __eq__ 方法比较 user1 和 user3 是否相等,并打印结果
    # 因为它们的 username 不相等,所以输出为 False
    print(user1 == user3)  # 输出: False

# 5、列表包装类(考虑顺序)
# 定义一个名为ListWrapper的类,用于包装列表
class ListWrapper:
    # 类的初始化方法,用于设置items属性
    def __init__(self, items):
        # items属性存储传入的列表
        self.items = items
    # 类的相等性比较方法,用于判断两个ListWrapper对象是否相等
    def __eq__(self, other):
        # 检查other是否是ListWrapper的实例
        if isinstance(other, ListWrapper):
            # 如果是,则比较两个ListWrapper对象的items属性是否相等
            # 这里使用了Python列表的相等性比较,即元素相同且顺序也相同
            return self.items == other.items
        # 如果other不是ListWrapper的实例,则返回False
        return False
# 如果当前脚本作为主程序运行(而不是被导入)
if __name__ == '__main__':
    # 创建三个ListWrapper对象,分别包装不同的列表
    lst1 = ListWrapper([1, 2, 3])  # 第一个ListWrapper对象,包装了列表[1, 2, 3]
    lst2 = ListWrapper([2, 1, 3])  # 第二个ListWrapper对象,包装了列表[2, 1, 3],与lst1元素相同但顺序不同
    lst3 = ListWrapper([1, 2, 3])  # 第三个ListWrapper对象,包装了列表[1, 2, 3],与lst1完全相同
    # 使用Python内置的相等性比较来比较lst1和lst2
    # 因为lst1和lst2的items属性(即包装的列表)元素相同但顺序不同,所以不相等
    print(lst1 == lst2)  # 输出:False
    # 使用Python内置的相等性比较来比较lst1和lst3
    # 因为lst1和lst3的items属性(即包装的列表)完全相同,所以相等
    print(lst1 == lst3)  # 输出:True

# 6、日期类(考虑年、月、日)
# 导入Python内置的date类,该类用于处理日期
from datetime import date
# 定义一个名为CustomDate的类,用于封装日期功能
class CustomDate:
    # 类的初始化方法,用于创建CustomDate对象时设置日期
    def __init__(self, year, month, day):
        # 使用Python内置的date类来创建一个日期对象,并将其存储在self.date属性中
        self.date = date(year, month, day)
    # 类的相等性比较方法,用于判断两个CustomDate对象是否相等
    def __eq__(self, other):
        # 检查other是否是CustomDate的实例
        if isinstance(other, CustomDate):
            # 如果是,则比较两个CustomDate对象的date属性是否相等
            # 这里使用了Python内置的date类的相等性比较
            return self.date == other.date
        # 如果other不是CustomDate的实例,则返回False
        return False
# 如果当前脚本作为主程序运行(而不是被导入)
if __name__ == '__main__':
    # 创建三个CustomDate对象,分别表示不同的日期
    date_1 = CustomDate(2024, 10, 24)  # 第一个CustomDate对象,表示2024年10月24日
    date_2 = CustomDate(2024, 10, 24)  # 第二个CustomDate对象,也表示2024年10月24日
    date_3 = CustomDate(2024, 10, 8)  # 第三个CustomDate对象,表示2024年10月8日
    # 使用Python内置的相等性比较来比较date_1和date_2
    # 因为date_1和date_2的date属性相同(都是2024年10月24日),所以相等
    print(date_1 == date_2)  # 输出:True
    # 使用Python内置的相等性比较来比较date_1和date_3
    # 因为date_1和date_3的date属性不同(分别是2024年10月24日和2024年10月8日),所以不相等
    print(date_1 == date_3)  # 输出:False

16、__exit__方法

16-1、语法
__exit__(self, exc_type, exc_value, exc_tb)
    return True  # 或者False或不返回任何值(即None)
    # 如果要忽略异常,则返回True
16-2、参数

16-2-1、self(必须)一个对实例对象本身的引用,在类的所有方法中都会自动传递。

16-2-2、exc_type(可选)异常类型,如果在with块中引发了异常,则此参数是异常的类型;否则

为None。

16-2-3、exc_value(可选)异常值,即with块中引发的异常的实例;如果未引发异常则为None。

16-2-4、exc_tb(可选)追踪信息(traceback object),它是一个指向引发异常位置的堆栈跟

踪;如果未引发异常则为None。

16-3、功能

        用于在退出一个由with语句控制的代码块时执行特定的操作。

16-4、返回值

16-4-1、返回True,那么它会抑制(忽略)异常,也就是说,即使with块中的代码引发了异常,这个异常也不会被传播到with语句之外。

16-4-2、返回False或不返回任何值(即None),那么with块中引发的异常将会按照正常的异常处理机制来处理。

16-5、说明

        无

16-6、用法
# 016、__exit__方法:
# 1、文件操作
# 定义一个名为 FileContextManager 的类,用于管理文件的上下文
class FileContextManager:
    # 初始化方法,接收一个文件名作为参数
    def __init__(self, filename):
        # 将传入的文件名保存到实例变量 filename 中
        self.filename = filename
    # 定义 __enter__ 方法,用于 with 语句执行时进入上下文时调用
    def __enter__(self):
        # 使用实例变量 filename 打开文件,并设置文件模式为只读 ('r')
        self.file = open(self.filename, 'r')
        # 返回打开的文件对象,以便在 with 语句的 as 子句中引用
        return self.file
    # 定义 __exit__ 方法,用于 with 语句执行完毕后退出上下文时调用
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 关闭之前打开的文件对象
        self.file.close()
 # 使用 with 语句和 FileContextManager 类来管理文件操作
# 'test.txt' 是要打开的文件名,'file' 是打开文件后在 with 语句内部使用的引用名
with FileContextManager('test.txt') as file:
    # 读取文件内容并打印,假设文件内容是 "Hello, World!"
    print(file.read())  # 输出:Hello, World!

# 2、数据库连接
# 假设有一个数据库连接库(但在这里我们只是模拟它)
class DatabaseContext:
    # 定义一个上下文管理器的进入方法
    def __enter__(self):
        # 当使用 with 语句进入上下文时,该方法会被调用
        # 这里只是一个示例,实际中会使用数据库连接库来建立真实的数据库连接
        print("Connecting to database...")
        # 假设我们有一个模拟的数据库连接字符串
        self.connection = "Mock Database Connection"
        # 返回连接对象(或连接字符串),以便在 with 语句的 as 子句中引用
        return self.connection
    # 定义一个上下文管理器的退出方法
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 当 with 语句块结束时,无论是否发生异常,该方法都会被调用
        print("Closing database connection...")
        # 在这里,我们只是打印了一条消息,但在真实情况下,你会关闭数据库连接
# 使用 with 语句和 DatabaseContext 类来管理数据库连接的上下文
with DatabaseContext() as db:
    # 在这里使用数据库连接(这里只是打印了一条消息)
    # 假设 db 是一个数据库连接对象,但在本例中它只是一个字符串
    print("Using database:", db)
    # 当 with 语句块结束时,__exit__ 方法会被自动调用,打印 "Closing database connection..."

# 3、临时目录
# 导入tempfile模块,用于创建临时文件和目录
import tempfile
# 导入shutil模块,用于删除目录及其内容
import shutil
# 定义一个名为TempDirContext的上下文管理类
class TempDirContext:
    # 定义__enter__方法,当使用with语句进入上下文时调用
    def __enter__(self):
        # 使用tempfile.mkdtemp()方法创建一个临时目录
        self.temp_dir = tempfile.mkdtemp()
        # 打印创建的临时目录的路径
        print(f"Created temp directory: {self.temp_dir}")
        # 返回临时目录的路径,以便在with语句的as子句中引用
        return self.temp_dir
     # 定义__exit__方法,当with语句块结束时调用
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 打印将要删除的临时目录的路径
        print(f"Deleting temp directory: {self.temp_dir}")
        # 使用shutil.rmtree()方法删除临时目录及其所有内容
        shutil.rmtree(self.temp_dir)
# 使用with语句和TempDirContext类来管理临时目录的上下文
with TempDirContext() as temp_dir:
    # 打印正在使用的临时目录的路径
    print(f"Using temp directory: {temp_dir}")
    # 在这里可以使用temp_dir变量来引用临时目录,并执行相关操作
    # 例如:在临时目录中创建文件、写入数据等
    # 注意:由于此代码片段是一个注释示例,所以没有包含实际的文件操作

# 4、锁机制
# 导入threading模块,用于多线程操作
import threading
# 定义一个名为ThreadLock的类,用于管理线程锁
class ThreadLock:
    # 初始化方法,创建一个threading.Lock对象
    def __init__(self):
        self.lock = threading.Lock()
    # 定义__enter__方法,当使用with语句进入上下文时调用
    def __enter__(self):
        # 尝试获取锁
        self.lock.acquire()
        # 打印锁已被获取的消息
        print("Acquired lock")
        # 返回锁对象(虽然通常不需要返回,但这里为了示例还是返回了)
        return self.lock
    # 定义__exit__方法,当with语句块结束时调用
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 释放锁
        self.lock.release()
        # 打印锁已被释放的消息
        print("Released lock")
# 使用with语句和ThreadLock类来管理线程锁
with ThreadLock() as lock:
    # 在这里进行线程安全的操作
    # 注意:因为with语句已经处理了锁的获取和释放,所以这里的lock变量其实不需要使用
    # 除非你想在with块内部检查锁的状态或做其他与锁相关的操作
    pass

# 5、设置和恢复环境变量
# 定义一个名为EnvVarContext的上下文管理器类,用于临时修改环境变量
class EnvVarContext:
    def __init__(self, var_name, new_value):
        # 初始化时接收环境变量名和新值
        self.var_name = var_name  # 存储要修改的环境变量名
        self.new_value = new_value  # 存储新的环境变量值
        self.old_value = None  # 用于存储原始的环境变量值
    def __enter__(self):
        # 当使用with语句进入上下文时调用
        # 获取环境变量原来的值
        self.old_value = os.environ.get(self.var_name)
        # 设置新的环境变量值
        os.environ[self.var_name] = self.new_value
        # 打印已设置新的环境变量值
        print(f"Set {self.var_name} to {self.new_value}")
        # 因为with语句的as子句没有使用变量来接收返回值,所以这里不需要返回具体的值
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 当with语句块结束时调用
        # 恢复环境变量为原来的值
        if self.old_value is not None:
            # 如果原值存在,则恢复为原值
            os.environ[self.var_name] = self.old_value
        else:
            # 如果原值不存在,则从环境变量中删除该变量
            del os.environ[self.var_name]
            # 打印环境变量已恢复为原始值或已删除
        print(f"Restored {self.var_name} to its original value")
# 导入os模块,以便操作环境变量
import os
# 使用with语句和EnvVarContext类来修改环境变量
with EnvVarContext('MY_VAR', 'new_value') as _:
    # 在with块内部,MY_VAR的值已被临时修改为'new_value'
    print(os.environ['MY_VAR'])  # 输出: new_value
# 退出with块后,MY_VAR被恢复为原来的值或删除
# 之后的代码可以确认MY_VAR的值是否已被恢复

# 6、模拟上下文状态管理
class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        # 在这里,你可以设置任何进入上下文时需要的状态或资源
        # 例如,打开一个文件、获取一个锁等
        self.resource = "some resource"  # 假设这是一个需要管理的资源
        return self  # 通常,__enter__ 应该返回一个对象,以便在 with 块中使用
    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")
        # 在这里,你可以执行任何清理工作,例如关闭文件、释放锁等
        # 你可以检查 exc_type, exc_value, traceback 来确定是否发生了异常
        # 如果发生了异常并且你想重新抛出它,则不应该在这里进行任何操作
        # 如果你想忽略异常,你可以在这里返回 True
        # 如果你想在退出时总是执行一些清理代码,无论是否发生异常,你可以将这里留空
# 使用 with 语句和上下文管理器
with MyContextManager() as manager:
    print("Inside the with block, accessing resource:", manager.resource)
    # 这里是代码块,当执行到这里时,__enter__ 已经被调用
    # 当这个代码块执行完毕(或者发生异常)时,__exit__ 将会被调用

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1716963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Idea的相关操作

1、关闭自动更新 点击左上角File->Setting,进入配置页面,点击Appearance & Behavior > System Settings > Updates,取消勾选更新选项,如图; 2、代码提示忽略大小写 点击左上角File->Setting&#xf…

Llama 3-V: 比GPT4-V小100倍的SOTA

大模型技术论文不断,每个月总会新增上千篇。本专栏精选论文重点解读,主题还是围绕着行业实践和工程量产。若在某个环节出现卡点,可以回到大模型必备腔调重新阅读。而最新科技(Mamba,xLSTM,KAN)则提供了大模…

【目标检测】基于深度学习的植物中草药智能识别系统【python源码+Pyqt5界面+数据集+训练代码 MX_001期】

系统简介: 这是一款基于深度学习技术的植物草药智能识别系统。系统通过分析植物草药的图像,能够准确地识别出不同种类的草药,并提供相关的信息和用途。用户只需将植物草药的图像上传至系统,即可快速获得识别结果。 系统利用先进…

海关接口源码:跨境贸易的数字桥梁

在全球化贸易日益频繁的今天,海关接口源码成为了促进国际贸易的关键技术之一。它不仅提高了通关效率,还确保了贸易数据的准确性和安全性。本文将探讨海关接口源码的重要性、功能以及其在现代贸易中的作用。 一、海关接口源码的重要性 海关接口源码是一…

C语言分支和循环(2)

我的相关博客: C语言的分支与循环(1) 1.switch语句 除了 if 语句外,C语⾔还提供了 switch 语句来实现分⽀结构。 switch 语句是⼀种特殊形式的 的 if...else 结构,⽤于判断条件有多个结果的情况。它把多重 else if…

大模型高级 RAG 检索策略:自动合并检索

节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

C++结构体数组struct和使用sizeof 从结构体数组中取出数据并写入数字功放寄存器编程实例

C结构体数组编程实例 C结构体数组与普通数组的不同之处:用至少1个花括号来分隔数组。 C结构体数组定义 C结构体数组的定义和定义结构体变量的方法类似,struct声明其为数组即可 结构体数组实例1: typedef struct {u8 cmd; //定义数组中的…

【MyBatis】MyBatis操作数据库(一)

目录 MyBatis的基础定义MyBatis配置相关文件一、注解操作数据库1.1 Insert(插入注解)1.2 Delete(删除注解)1.3 Update(修改注解)1.4 Select(重点:查询注解)注解解决查询不匹配问题拓展:Param(重命名注解)和OPtions(自增注解) 二、 XML操作数据库2.1 xml实…

迷你主机Esxi 6.7挂载新硬盘

背景 硬件:零刻SER Pro 6 系统:vmware Exsi 6.7.0 Update 3 现有的硬盘槽位占满了,但空间不够用,想要通过USB外接移动硬盘来进行扩容。使用了一块250G的硬盘做测试。 步骤 TL;DR # 停止usbarbitrator服务 /etc/init.d/usbarbi…

Unity2D横版摄像机跟随

在Unity2D横版游戏中,摄像机跟随是一个非常重要的功能。一个流畅的摄像机跟随系统可以让玩家更好地沉浸在游戏世界中。本文将介绍如何在Unity中实现2D横版摄像机跟随,并分享一些优化技巧。 一、准备工作 在开始实现摄像机跟随之前,请确保您…

分享 ASP.NET Core Web Api 中间件获取 Request Body 两个方法

不废话&#xff0c;直接上正文。_ 方法一 思路&#xff1a;利用 BodyReader 直接读取 HttpContext 的 Request Body&#xff0c;再反序列化 var reqStream context.Request.BodyReader.AsStream(); var jsonObj JsonSerializer.Deserialize<CheckAndParsingMiddlewareM…

宏集JMobile Studio—实现HMI界面高自由度设计

一、简介 物联网HMI的组态软件是数据可视化的重要工具&#xff0c;工程师可以通过图形化界面来配置、监控和管理现场采集的数据。目前&#xff0c;市面上大多数的组态软件里的可视化控件库都由设计师预先部署&#xff0c;用户只能调用而不能完全自定义控件&#xff0c;导致可视…

万界星空科技MES系统功能介绍

制造执行系统或MES 是一个全面的动态软件系统&#xff0c;用于监视、跟踪、记录和控制从原材料到成品的制造过程。MES在企业资源规划(ERP) 和过程控制系统之间提供了一个功能层&#xff0c;为决策者提供了提高车间效率和优化生产所需的数据。 万界星空科技MES 系统基础功能&am…

OpenCv之简单的人脸识别项目(登录页面)

人脸识别 一、项目准备二、登录页面1.导入所需的包2.设置窗口2.1定义窗口外观和大小2.2设置窗口背景2.2.1设置背景图片2.2.2创建label控件 3.运行脚本3.1定义识别脚本3.2定义提取脚本3.3定义标注脚本3.4定义人脸比对脚本3.5定义动态处理脚本3.6定义属性判断脚本 4.创建一个退出…

HTML跳动的爱心

目录 写在前面 HTML简介 跳动的爱心 代码分析 运行结果

eclipse-向Console控制台输出信息

首先这里主要用到的是org.eclipse.ui.console这个包&#xff0c;所以现在顺道先来了解一下&#xff1a; org.eclipse.ui.console是一个可扩展的console视图插件&#xff0c;利用它可以实现各种console&#xff0c;并把它们显示出来。该插件本身就实现了一个Message Console&…

2024瑜伽馆玩赚抖音,打造O2O线上线下超级门店

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89360427 更多资源下载&#xff1a;关注我。 课程介绍&#xff1a; 通过学习可以学会同城引流实操&#xff0c;通过线上放大实体门店盈利能力。清晰了解同城账号短视频的正确内容生产方向&#xff0c;并…

【记忆化搜索 】2312. 卖木头块

本文涉及知识点 记忆化搜索 LeetCode2312. 卖木头块 给你两个整数 m 和 n &#xff0c;分别表示一块矩形木块的高和宽。同时给你一个二维整数数组 prices &#xff0c;其中 prices[i] [hi, wi, pricei] 表示你可以以 pricei 元的价格卖一块高为 hi 宽为 wi 的矩形木块。 每…

如何解决 YUM源GPG密钥缺失:Public key for 猫头虎.rpm is not installed

如何解决 YUM源GPG密钥缺失&#xff1a;Public key for 猫头虎.rpm is not installed 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝…

史上最全网络安全面试题+答案

1、什么是SQL注入攻击 前端代码未被解析被代入到数据库导致数据库报错 2、什么是XSS攻击 跨站脚本攻击 在网页中嵌入客户端恶意脚本&#xff0c;常用s语言&#xff0c;也会用其他脚本语言 属于客户端攻击&#xff0c;受害者是用户&#xff0c;网站管理员也属于用户&#xf…