Python with语句与上下文管理器详解
本文深入解析Python中with语句的工作原理及其在资源管理中的应用。通过对比传统try-finally代码,阐述with语句如何自动执行__enter__和__exit__方法以释放资源。同时介绍利用contextlib模块和装饰器自定义上下文管理器的方法,帮助开发者编写更简洁、安全的代码。
Python with语句基础与资源管理
Python中的with语句主要用于资源访问场景,确保无论处理过程中是否发生错误或异常,都会执行规定的__exit__(清理)操作,从而释放被访问的资源。常见的应用场景包括文件读写后自动关闭、线程中锁的自动获取与释放等。
理解with语句涉及几个核心概念:上下文管理协议、上下文管理器、运行时上下文、上下文表达式以及处理资源的代码段。
with语句的应用场景
with语句的应用场景
编程中存在大量配套使用的操作,这类流程可称为计算过程。Python语言为此专门设计了with语句结构。文件处理便是此类计算过程的典型代表。
使用with语句前后的代码对比
使用with语句前后对比
在未使用with语句之前,打开文件的常规写法如下:
try:
# 1. [进入]
f = open('a.txt', 'r', encoding="utf-8")
# 2. [执行]
print(f.read())
finally:
if f:
# 3. [退出]
f.close()
Python操作文件通常遵循以下三个步骤:
1. [进入]以只读方式打开文件
若文件不存在,open()函数将抛出IOError错误,并提供错误码及详细信息提示文件缺失。
2. [执行]读取文件内容
若文件打开成功,调用read()方法可一次性读取全部内容。Python将内容加载至内存,并以str对象形式表示。
3. [退出]关闭打开的文件
文件使用完毕后必须关闭,因为文件对象会占用操作系统资源,且操作系统同时能打开的文件数量有限。
思考:为何关闭文件操作必须放在finally语句中?
由于文件读写时可能产生IOError,一旦出错,后续的f.close()便不会执行。因此,为确保无论是否出错都能正确关闭文件,可使用try ... finally结构来实现。
发现共性:
我们发现这类过程化语句具有共性:在进入一个片段前时必须执行某种操作,处理工作后后需要执行结束操作。例如上述代码:
finally:
if f:
f.close()
即可进行封装处理。
使用with语句后,打开文件的方式变为:
with open("a.txt", "r", encoding="utf-8") as f:
print(f.read())
该with语句与前述try ... finally结构功能一致,但代码更为简洁,且无需显式调用f.close()方法。
with语句的执行原理与上下文管理器
with语句的执行原理
从解释器角度理解with语句的执行流程。
with语句的基本形式为:
with 表达式 as 变量:
语句块
此类代码称为一个上下文(context)。执行with语句时,解释器先计算表达式的值,该值(对象)为上下文管理器,并假设该对象拥有如下类构造方法:
def __enter__():
# 描述进入上下文的动作
pass
def __exit__():
# 描述退出上下文的动作
pass
with语句在获取上下文管理器对象后,自动执行进入方法,并将返回值赋值给as后的变量,随后执行语句块。退出上下文前,自动执行对象的退出方法。
Python系统及标准库中部分类型已定义这对操作,可直接用于with语句。例如文件对象直接支持此对操作,因此可置于with语句头部。
若你有类似的计算过程需抽取,可自定义类并包含进入与退出方法。
使用contextlib自定义上下文管理器
自定义open函数
实际实现中发现,使用装饰器和生成器即可很好地解决该问题,无需通过类构造方法实现:
import contextlib # 引入上下文管理包
@contextlib.contextmanager # 给函数引入装饰器
def myopen(dir, mode):
print("开始")
f = open(dir, mode, encoding='utf-8')
try: # 上文
yield f
finally: # 下文
print("结束")
f.close()
with myopen("a.txt", 'r') as fobj: # 把try中的yield中的f赋值给fobj
# with会将with后面的函数中的yield赋值给fobj
for i in fobj:
print(i)
# 等待上面的循环结束后,才最终执行finally的代码,所以这就是上下文管理
输出结果如下:
开始
hello,我是a.txt的第1行文字。
结束
with语句总结与最佳实践
总结
文件读写、使用pickle包进行数据存储与恢复等操作,均非常适合使用with语句。
pickle包的使用案例:
try:
with open("phone.pickle", "wb") as outf:
pickle.dump("13193388105", outf)
except:
print("file have errow.")
try:
with open("phone.pickle", "rb") as outf:
data = pickle.load(outf)
print(type(data))
print(data)
except:
print("file have errow.")
总结使用with语句的优点:
- 采用with语句的代码更加简洁。
- 避免因忘记调用f.close()而引发的错误。
- 若对象操作包含进入与退出过程,可将其抽取并实现自动化执行。
总结
以上介绍了Python with语句及过程抽取思想,希望能对大家有所帮助。如有任何疑问,请留言,小编会及时回复。感谢大家对本站的支持!若觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













