Python之上下文管理器

aside section ._1OhGeD · · 671 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

1.引入

我们时常会看到网上很多人写文件读写的代码的时候,常常是这么写的:

with open('test.txt', 'r') as f:
    print(f.read())
    # 其它操作

这个with有什么用呢?
我们来看看不用这种写法我们怎么写。
假如我们直接这么写:

f = open('test.txt', 'r') 
print(f.read())
# 其它操作
f.close()

这种写法有什么问题呢?
假如在做其它操作的时候,出错了,程序终止,f.close()不会执行,则f这个句柄会一直得不到释放,要是并发操作,这个就很严重了。
所以我们通常会这么写:

try:
    f = open('test.txt', 'r')
except Exception:
    pass
finally:
    f.close()

但是每个操作都要这么写,就很繁琐了。
with open('test.txt', 'r') as f的作用在于,如果发生异常,它会帮你执行f.close()。它的原理就是上下文管理器。

这里还有个需要注意的问题,假如你用write()来写入文件,操作系统通常是先写到内存,空闲的时候再写入磁盘。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。

再举个例子,假如你写了一个类,但是里面可能会抛出异常,如果异常你需要做一些收尾操作,比如关闭数据库,关闭文件句柄,关闭某些长连接。一个你可以try catch,两个你也忍,多少太多地方用到了你就晕了,复制黏贴复制黏贴,累。

于是上下文管理器应运而生。

2. 上下文管理器--with关键字

  • 基本语法:
with EXPR as VAR:
    BLOCK
  • 运行逻辑:
    1.执行EXPR的上下文管理器的 __enter__方法,as VAR可以省略,如果不省略,则将__enter__方法的返回值赋值给VAR
    2.执行代码块BLOCK
    3.调用上下文管理器中的的__exit__方法

  • 例子:

class FileHelper:
    def __init__(self, file_name):
        self.file_name = file_name
        self.file_handler = None

    def __enter__(self):
        print('-' * 10, 'enter', '-' * 10)
        self.file_handler = open(self.file_name, 'a+')
        return self.file_handler

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('-' * 10, 'exit', exc_type, exc_val, exc_tb)

with FileHelper('test.txt') as f:
    for line in f:
        print(line)
    raise IOError

执行结果:

---------- enter ----------
Traceback (most recent call last):
---------- exit <class 'OSError'>  <traceback object at 0x0000027AF786AF88>
  File "D:/PythonProjects/Test.py", line 30, in <module>
    raise IOError
OSError

可以看到,结果输出了enter和exit的打印输出。

3.异步上下文管理器--async with

旧的上下文管理器是这样实现的:

class Manager:
    async def __aenter__(self):
          

    async def __aexit__(self, exc_type, exc, tb):
        

新语法:

async with EXPR as VAR:
    BLOCK

例子:

async def do_task(domain, pageUrl):
    async with aiohttp.ClientSession() as session:
        async with session.request('GET', pageUrl) as resp:
            if resp.status != 200:
                raise Exception('http error, url:{} code:{}'.format(pageUrl, resp.status))
                html = await resp.read()  # 可直接获取bytes

这是我另一篇文章Golang协程与Python协程速度比较贴过来的代码,await 相当于 yield from


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:aside section ._1OhGeD

查看原文:Python之上下文管理器

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

671 次点击  
加入收藏 微博
上一篇:Go 微服务实践
1 回复  |  直到 2019-10-11 21:56:50
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传