一、自定义上下文管理器
1.1 基本概念
**上下文管理协议 (Context management protocol):**包含 __enter__() 和 __exit__() 方法。
**上下文管理器 (Context manager):**实现 __enter__() 和 __exit__() 方法的对象。上下文管理器用于 定义执行 with 语句时要建立的运行时上下文,并负责执行 with 语句块 上下文的进入与退出 操作。通常使用 with 语句调用上下文管理器,也可通过直接调用其方法来使用。
**运行时上下文 (Runtime context):**由上下文管理器创建通过__enter__() 和 __exit__() 方法方法实现进入和退出。其中,__enter__() 方法在语句体执行开始前进入运行时上下文, __exit__() 在语句体执行结束后退出运行时上下文。with 语句支持“运行时上下文”这一概念。
**上下文表达式 (Context expression):**with 语句中跟在关键字 with 后的表达式。该表达式返回一个上下文管理器对象。
**语句体 (with-body):**即 with 语句中的代码块。执行语句体前,会调用上下文管理器的 __enter__() 方法;执行语句体后,会调用上下文管理器 __exit__() 方法。
# with语句的语法格式
with EXPR as VAR:
with-body
1.2 自定义文件读写上下文管理器
# -*- coding: utf-8 -*-
# @Time : 2021/11/27 12:39
# @Author : wljess
# @File : 上下文管理器.py
# @Describe :
# @Software : PyCharm
class Context:
def __init__(self, name, mode):
self.name = name
self.mode = mode
# 文件句柄,资源对象
self.file_handle = None
def __enter__(self):
# 进入上下文管理器的运行时上下文,获取资源
print("__enter__")
self.file_handle = open("info.txt", "w")
return self.file_handle
def __exit__(self, exc_type, exc_val, exc_tb):
# 退出上下文管理器的运行时上下文,释放资源
print("__exit__")
self.file_handle.close()
with Context("info.log", 'w') as f:
print("业务逻辑主体部分")
f.write("hello world!!!")
'''
上下文管理器调用解析:
1. with语句先暂存Context类的__exit__方法,调用Context类的 __enter__ 方法 (进入上下文管理器的运行时上下文)。
2. __enter__方法打开文件info.log文件并返回给with语句,而打开的文件句柄被传递给变量f。
3.执行with语句体/代码块,令f调用write()方法在文件info.log中写入"hello world!!!" 。
4.with语句调用先前暂存的__exit__方法 (退出上下文管理器的运行时上下文),正常关闭文件info.log文件。
'''
1.3 上下文管理器捕获异常
# -*- coding: utf-8 -*-
# @Time : 2021/11/27 15:11
# @Author : wljess
# @File : 上下文管理器捕获异常.py
# @Describe :
# @Software : PyCharm
class Context:
def __init__(self, name, mode):
self.name = name
self.mode = mode
# 文件句柄,资源对象
self.file_handle = None
def __enter__(self):
# 进入上下文管理器的运行时上下文,获取资源
print("__enter__")
self.file_handle = open(self.name, self.mode)
return self.file_handle
def __exit__(self, exc_type, exc_val, exc_tb):
'''
:param exc_type: 异常类型
:param exc_val: 异常值
:param exc_tb: 异常回溯追踪
:return:
'''
# 退出上下文管理器的运行时上下文,释放资源
print("__exit__")
if exc_val:
print(exc_type)
print(exc_tb)
print(exc_val)
self.file_handle.close()
return True
with Context("info.txt", 'r') as f:
print("业务逻辑主体部分")
# 调用不存在的undefinded_method方法时,抛出了异常
f.undefinded_method("Bye")
'''
__exit__()方法中捕捉的异常,实际上是with语句体(with代码块)所抛出的异常,默认__exit__()方法返回值为None,
若__exit__()方法返回 True,则说明该异常已被“处理”,无需再处理
若__exit__()方法返回 True 之外的任何内容,则该异常将被 with 语句抛出
'''
二、基于生成器的上下文管理器
# -*- coding: utf-8 -*-
# @Time : 2021/11/27 16:08
# @Author : wljess
# @File : 基于生成器的上下文管理器.py
# @Describe :
# @Software : PyCharm
from contextlib import contextmanager
@contextmanager
def file_manager(name, mode):
try:
f = open(name, mode)
yield f
except Exception as error:
print(type(error))
print(error)
finally:
f.close()
with file_manager("info.txt", "r") as file:
content = file.read()
print(content)
三、上下文管理器应用场景
上下文管理器的典型用途包括:保存和恢复各种全局状态、锁定和解锁资源、关闭已打开的文件 等。
# mysql数据库连接
# -*- coding: utf-8 -*-
# @Time : 2021/11/27 17:00
# @Author : wljess
# @File : 上下文管理器实现mysql连接.py
# @Describe :
# @Software : PyCharm
import pymysql
class DataBase:
def __init__(self, host, username, pwd, db_name):
self.coon = pymysql.connect(host=host, user=username, passwd=pwd, database=db_name)
self.cursor = self.coon.cursor()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.coon.close()
self.cursor.close()
def query(self, sql):
self.cursor.execute(sql)
result = self.cursor.fetchone()
return result
with DataBase("localhost", "root", "", "mydb") as db:
print(db.query("select name,age,salary from user_table where id=4947;"))
# 管理多个资源
# 我们要同时打开两个文件进行处理,不优雅的写法
with open("file1", "r") as f1:
with open(file2, "w") as f2:
f2.write(f1.read())
# 优雅的写法
with open("file1", "r") as f1, open("file2", "w") as f2:
f2.write(f1.read())
# -*- coding: utf-8 -*-
# @Time : 2021/11/27 17:00
# @Author : wljess
# @File : 上下文管理器管理锁.py
# @Describe :
# @Software : PyCharm
import time
import threading
class Singleton:
"""单例模式————最多只允许创建一个该类实例"""
_lock = threading.Lock()
_instance = None
def __new__(cls, *args, **kwargs):
time.sleep(1) # 模拟高并发情况
with cls._lock:
if not cls._instance:
cls._instance = object.__new__(cls)
return cls._instance
def task(a, b):
s = Singleton(a, b)
print(s)
for i in range(5):
t = threading.Thread(target=task, args=(2, 3))
t.start()