深入解析 Python 中的上下文管理器

通常我们希望把一些操作放到一个代码块中,在代码块中执行时就可以保持在某种运行状态,而当离开该代码块时就执行另一个操作,结束当前状态;所以,简单来说,上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取“处理”。Python提供了不同的方法来管理执行时间。例如,您可以使用Python的内置timeit模块来管理一小段代码的执行时间。

>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.3018611848820001

但是,timeit.timeit函数仅接受字符串,如果要管理比较复杂的函数时会有局限性。以下示例向您展示如何使用timeit模块运行和管理函数。

def test():
    """Stupid test function"""
    L = [i for i in range(100)]


if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

尽管它可以工作,但看起来并不是真正的pythonic。管理执行时间的另一种方法是利用Python的内置cProfile模块,但是并不建议用它,实际上它不是很精确,这只是一种变通方法,可让您了解某些代码段需要执行多长时间。您可以通过以下方式使用它:

>>> python -m cProfile <file_name.py>

既然上面的两种方法都不是非常Pythonic并且都有缺陷,那么我们如何实现一个比较完美的解决方案呢?

其实很简单:我们只要能拿到程序开始执行和结束执行的时间就可以了,下面介绍具体方法,Python有一个内置模块可供我们使用:time

>>> import time
>>> start = time.time()
>>> # do some stuff
>>> end = time.time()
>>> print(f"Elapsed Time: {end - start}")

但是这样写不是很方便。我们可以创建一个上下文管理器。

创建一个上下文管理器

使用Python创建上下文管理器有两种不同方法,我们将研究两种方法来实现此目的:基于类和基于生成器的上下文管理器。

基于类的上下文管理器

要创建基于类的上下文管理器,需要先实现魔法变量__enter____exit__。进入上下文(或代码块)时调用第一个,离开上下文时调用后者。

有了这些准备,我们就可以来创建一个实现这两种方法的Timer类。进入代码块时,我们希望获取当前时间并将其保存到表示开始的变量中。如果我们离开代码块,我们想获取当前时间并从中减去开始时间。结果被打印出来。

为了自定义输出,我们让用户指定一个语句,该语句在经过的时间之前打印。以下要点向您展示了一个即用型的类。

from time import time


class Timer(object):
    def __init__(self, description):
        self.description = description
    def __enter__(self):
        self.start = time()
    def __exit__(self, type, value, traceback):
        self.end = time()
        print(f"{self.description}: {self.end - self.start}")


with Timer("List Comprehension Example"):
    s = [x for x in range(10_000_000)]

基于生成器的上下文管理器

基于生成器的方法更加简单。我们可以创建一个包含程序流程的生成器函数(获取开始和结束时间以及打印经过的时间)。@contextmanager装饰器通过使用GeneratorContextManager对象包装生成器,将生成器功能转换为适当的上下文管理器。

from contextlib import contextmanager
from time import time


@contextmanager
def timing(description: str) -> None:
    start = time()
    yield
    ellapsed_time = time() - start

    print(f"{description}: {ellapsed_time}")


with timing("List Comprehension Example"):
    s = [x for x in range(10_000_000)]

如果执行了with后面的代码块,将跳回到yield关键字之后的位置继续执行。

总结

在本文中,我们学习了如何创建自己的时间上下文管理器。了解基本概念,我们可以通过两种方式实现上下文管理器:基于类和基于生成器。生成的类和生成器函数可以直接使用。

推荐阅读:

2020Python招聘内推渠道开启啦!

老司机教你5分钟读懂Python装饰器

用Python实现粒子群算法

抄底美股?用Python分析美股实际收益率

▼点击成为社区会员   喜欢就点个在看吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值