社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

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

Python中文社区 • 4 年前 • 655 次点击  

通常我们希望把一些操作放到一个代码块中,在代码块中执行时就可以保持在某种运行状态,而当离开该代码块时就执行另一个操作,结束当前状态;所以,简单来说,上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取“处理”。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 

既然上面的两种方法都不是非常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分析美股实际收益率


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

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/62423
 
655 次点击