上下文管理器之contextlib模块

697 阅读3分钟

1.模块简介

上下文管理器就是允许你可以自动地开始和结束一些事情。最常用的场景就是打开文件、写入内容、关闭文件了。当你使用with语句时,python会自动创建一个上下文管理器

with open(r'new.txt') as f:
    f.write('hello world')

上下文管理器背后的工作机制是采用python的方法:enterexit

2.模块使用

2.1 自定义创建一个上下文管理器类

import sqlite3
​
​
class Dao(object):
​
    def __init__(self, db):
        self.db = db
​
    def __enter__(self):
        self.connect = sqlite3.connect(self.db)
        return self.connect
​
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.connect.close()
​
​
if __name__ == '__main__':
    db = 'test/test.db'
    with Dao(db) as dao:
        dao.cursor()

这里创建一个数据库操作的类,传入数据库路径后,将自动执行__enter__()方法,为我们创建一个数据库对象。当结束数据库操作准备退出时, exit()会自动执行关闭这个链接

2.2 利用contextlib创建一个上下文管理器

from contextlib import contextmanager
​
​
@contextmanager
def file_handle(file_path):
    global file_object
    try:
        file_object = open(file_path, "w")
        yield file_object
    except:
        print("file error")
    finally:
        print("colse file")
        file_object.close()
​
​
if __name__ == "__main__":
    with file_handle("test_123.txt") as f:
        f.write("上下文管理器")

3 其他类

3.1 colsing类

一旦代码运行完毕,该类就会将事件关闭。

from contextlib import closing
import requests
​
with closing(requests.get(r'https://www.baidu.com/')) as data:
    print(data.status_code)

这里在closing中访问百度首页, 当访问结束后,指向该页面的句柄就会自动关闭

3.2 suppress类

suppress类的作用主要是为了禁止任意数目的异常。比如我们想要忽略FileNotFoundError异常,普通的with语句就会报错

with open(r'1.txt') as f:
    print(f.read())

result:

Traceback (most recent call last):
  File "D:/ywb/123.py", line 1, in <module>
    with open(r'1.txt') as f:
FileNotFoundError: [Errno 2] No such file or directory: '1.txt'

如果要使得程序忽略这个异常,可以按照如下的方式进行:

from contextlib import suppress
​
with suppress(FileNotFoundError):
    with open(r"1.txt") as f:
        print(f.read())
print('end......')

result:

end......

3.3 redirect_stdout类

该类主要是用于标准输出重定向,举例如下:

运行前:

image-20211216140747112.png

代码;

from contextlib import redirect_stdout
​
path = "test.txt"with open(path,"w") as fobj:
    with redirect_stdout(fobj):
        print(redirect_stdout)

运行后:

image-20211216140821706.png

image-20211216140834101.png

3.4 redirect_stderr类

该类主要是用于标准错误重定向,举例如下:

运行前:

image-20211216140821706.png

代码;

from contextlib import redirect_stderr
​
path = "test2.txt"with open(path,"w") as fobj:
    with redirect_stderr(fobj):
        print(redirect_stderr)

运行后:

image-20211216140942474.png

image-20211216140932548.png

3.5 ExitStack类

from contextlib import ExitStack
​
filenames = ["1.txt", "2.txt"]
with ExitStack as stack:
    for filename in filenames:
        with open(filename) as f:
            file_objects = stack.enter_context(f)

3.6 可重用的上下文管理器

大部分情况下创建的上下文管理器之只能使用一次,没法进行多次调用。

from contextlib import contextmanager
​
​
@contextmanager
def context():
    print("yield")
    yield
    print("上下文管理器已存在")
​
​
context1 = context()
with context1:
    pass
print("============================")
with context1:
    pass

result:

yield
​
上下文管理器已存在
============================
​
Traceback (most recent call last):
  File "D:/ywb/123.py", line 15, in <module>
    with context1:
  File "D:\ThsSoftware\Python_ths\ths\lib\contextlib.py", line 111, in __enter__
    del self.args, self.kwds, self.func
AttributeError: args
​
Process finished with exit code 1

如果我们需要让其成为可以重用的上下文管理器,需要结合redirect_stdout进行操作

from contextlib import redirect_stdout
from io import StringIO
​
stream = StringIO()
write_to_steam = redirect_stdout(stream)
​
​
with write_to_steam:
    print("第一次调用")
print(stream.getvalue())
print('===================')
with write_to_steam:
    print("第二次调用")
print(stream.getvalue())

result:

第一次调用
​
===================
第一次调用
第二次调用

这里我们创建了一个上下文管理器,他们都向StringIO中写入数据。由于redirect_stdout可重用,所以这段代码没有抛出异常。但是这种可以重用的上下文管理器不一定是线程安全的。如果线程中要使用,需要结合具体情景再做处理