Python 上下文管理器

0.概述

上下文管理器是定义在语句中使用with语法执行的运行时上下文对象

1.概念

从一个简单的示例开始,来理解上下文管理器的概念。

假设有一个名为data.txt包含整数100的文件。

以下程序读取data.txt文件,将其内容转换为数字,并将结果显示为标准输出:

f = open('data.txt')
data = f.readlines()

# convert the number to integer and display it
print(int(data[0]))

f.close()

代码简单明了。但data.txt可能包含无法转换为数字的数据。在这种情况下,代码将导致异常。

例如,如果data.txt包含字符串'100'而不是数字 100,则会收到以下错误:

ValueError: invalid literal for int() with base 10: "'100'"

由于此异常,Python 可能无法正确关闭文件。

要解决此问题,可以使用try...except...finally声明:

try:
    f = open('data.txt')
    data = f.readlines()
    # convert the number to integer and display it
    print(int(data[0]))
except ValueError as error:
    print(error)
finally:
    f.close()

由于finally块中的代码始终执行,因此代码将始终正确关闭文件。

此解决方案按预期工作。但是,它过于冗长。

因此,Python 提供了一种更好的方法,允许在完成处理文件后自动关闭文件。

这就是上下文管理器发挥作用的地方。

下面显示了如何使用上下文管理器来处理data.txt文件:

with open('data.txt') as f:
    data = f.readlines()
    print(int(data[0])    

在此示例中,我们将open()函数与with语句一起使用。执行块后,Python 将自动关闭。

2.with语句

以下是with语句的典型语法:

with context as ctx:
    # use the the object 

# context is cleaned up

它的工作流程如下:

  • 当 Python 遇到该with语句时,它会创建一个新上下文。上下文可选择返回一个object
  • 在with块之后,Python 会自动清理上下文。
  • ctx的作用域与with语句的作用域相同。这意味着可以在with内部和之后访问ctx

下面显示了如何访问语句后面的变量:

with open('data.txt') as f:
    data = f.readlines()
    print(int(data[0]))


print(f.closed)  # True

3.上下文管理器协议

Python 上下文管理器基于上下文管理器协议工作。

上下文管理器协议具有以下方法:

  • __enter__()– 设置上下文并选择性地返回一些对象
  • __exit__()– 清理对象。

如果希望一个class支持上下文管理器协议,则需要实现这两种方法。

假设ContextManager是一个支持上下文管理器协议的类。

下面演示如何使用该ContextManager类:

with ContextManager() as ctx:
    # do something
# done with the context

ContextManager 与with语句一起使用时,Python 会隐式创建 ContextManager 类的一个实例(临时对象),并在该实例上自动调用__enter__()方法。

__enter__()方法可以选择返回一个对象。如果是这样,Python 会为返回的对象分配 ctx

需要注意,ctx引用了__enter__()方法返回的对象。它并不引用ContextManager 类的实例。

如果在 with 块内或块之后发生异常,Python 将在实例对象上调用__exit__()方法。

Python 上下文管理器

从功能上讲,该语句等效于以 下 try...finally 语句:

instance = ContextManager()
ctx = instance.__enter__()

try:
    # do something with the txt
finally:
    # done with the context
    instance.__exit__()

__enter__() 方法

__enter__()方法中,可以执行必要的步骤来设置上下文。

(可选地是),可以从__enter__()方法返回一个对象。

__exit__() 方法

Python 始终执行__exit__()方法,即使块中发生异常也是如此。

__exit__()方法接受三个参数:异常类型、异常值和回溯对象。如果没有异常发生,所有这些参数都将是None

def __exit__(self, ex_type, ex_value, ex_traceback):
    ...

__exit__()方法返回一个布尔值,即 TrueFalse

如果返回值为 True,则 Python 将使任何异常保持静默。否则,它不会报出异常。

4.上下文管理器应用程序

从前面的示例中可以看出,上下文管理器的常见用法是自动打开和关闭文件。

但是,也可以在许多其他情况下使用上下文管理器:

1)Open – Close

如果要自动打开和关闭资源,可以使用上下文管理器。

例如,可以使用上下文管理器打开套接字并关闭它。

2) Lock – release

上下文管理器可以帮助更有效地管理对象的锁,它们允许获取锁并自动释放它。

3) Start – stop

上下文管理器还可以帮助用户处理需要启动和停止阶段的方案。

例如,可以使用上下文管理器来启动计时器并自动停止计时器。

3) Change – reset

上下文管理器可以使用更改和重置方案。

例如,应用程序需要连接到多个数据源。它有一个默认连接。

要连接到另一个数据源,请执行以下操作:

  • 首先,使用上下文管理器将默认连接更改为新连接。
  • 其次,使用新连接工作
  • 第三,在完成使用新连接后将其重置回默认连接。

5.实现 Python 上下文管理器协议

下面显示了使用上下文管理器协议的open()函数的简单实现:

class File:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print(f'Opening the file {self.filename}.')
        self.__file = open(self.filename, self.mode)
        return self.__file

    def __exit__(self, exc_type, exc_value, exc_traceback):
        print(f'Closing the file {self.filename}.')
        if not self.__file.closed:
            self.__file.close()

        return False


with File('data.txt', 'r') as f:
    print(int(next(f)))

它是如何工作的。

  • 首先,使用__init__()方法初始化filenamemode
  • 其次,在__enter__()方法中打开文件并返回文件对象。
  • 第三,如果文件在__exit__()方法中打开,则关闭该文件。

6.使用上下文管理器实现启动和停止模式

下面定义了一个支持上下文管理器协议的Timer类:

from time import perf_counter


class Timer:
    def __init__(self):
        self.elapsed = 0

    def __enter__(self):
        self.start = perf_counter()
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.stop = perf_counter()
        self.elapsed = self.stop - self.start
        return False

它是如何工作的。

  • 首先,从time模块中导入perf_counter
  • 其次,在timer计时器中启动__enter__()方法
  • 第三,使用_exit__()方法停止计时器并返回消耗的时间。

现在,可以使用Timer类来测量计算 1000 斐波那契所需的时间,即执行 100 万次的耗时:

def fibonacci(n):
    f1 = 1
    f2 = 1
    for i in range(n-1):
        f1, f2 = f2, f1 + f2

    return f1


with Timer() as timer:
    for _ in range(1, 1000000):
        fibonacci(1000)

print(timer.elapsed)

7.小结

  • 在语句中执行with时,使用 Python 上下文管理器定义运行时上下文。
  • 应用__enter__()与__exit__() 方法以实现 上下文管理器协议。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

scott198512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值