Python with语句上下文管理器两种实现方法分析
本文深入解析Python中with语句背后的上下文管理器机制。通过对比传统try...finally写法,展示使用内置file类型及自定义类的简洁性。详细讲解两种实现方式:一是通过定义__enter__和__exit__方法的类实现,涵盖异常处理逻辑;二是利用contextlib模块的装饰器与生成器实现更优雅的控制流。此外,还介绍了在单个with语句中管理多个上下文的方法,帮助开发者写出更健壮、清晰的代码。
Python上下文管理器概述
本文详细解析Python中with语句所依赖的上下文管理器机制,旨在为读者提供实用的参考与深入理解。
在软件开发过程中,常遇到此类场景:在执行特定代码块前需执行初始化操作,而在代码块结束后需执行清理操作。典型的例子包括文件读写后必须关闭文件句柄、数据库操作完成后断开连接,以及资源加锁与解锁等场景。
针对上述需求,Python引入了上下文管理器(Context Manager)的概念。借助上下文管理器,开发者可以明确定义代码块执行前的准备动作以及执行后的收尾动作,从而规范资源管理流程。
为何使用上下文管理器
一、为何使用上下文管理器
1、不使用上下文管理器的情况
通常,开发者需要通过try...finally语句来执行异常处理及关闭句柄的操作,以确保资源释放。
logger = open("log.txt", "w")
try:
logger.write('Hello ')
logger.write('World')
finally:
logger.close()
print logger.closed
2、使用上下文管理器
Python内置的file类型默认支持上下文管理协议。使用with语句编写代码,使得逻辑结构更加精简清晰。
with open("log.txt", "w") as logger:
logger.write('Hello ')
logger.write('World')
print logger.closed
上下文管理器的两种实现方式
二、实现上下文管理器实现上下文管理器有两种方式实现。方法一:类实现__enter__和__exit__方法。方法二:contextlib模块装饰器和生成器实现。
接下来,我们将通过具体示例,分别演示这两种方法如何实现自定义的上下文管理器。
通过类实现__enter__和__exit__方法
1、方法一:通过类实现__enter__和__exit__方法
class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, traceback):
self.file_obj.close()
with File('demo.txt', 'w') as opened_file:
opened_file.write('Hola!')
在类中实现__enter__和__exit__方法后,即可利用with语句进行上下文管理。
a、底层都发生了什么?
1、with语句先暂存了File类的__exit__方法,然后它调用File类的__enter__方法。 2、__enter__方法打开文件并返回给with语句,打开的文件句柄被传递给opened_file参数。 3、with语句调用之前暂存的__exit__方法,__exit__方法关闭了文件。
b、异常处理
关于异常处理,with语句会采取哪些步骤。
1. 它把异常的type,value和traceback传递给__exit__方法 2. 它让__exit__方法来处理异常 3. 如果__exit__返回的是True,那么这个异常就被忽略。 4. 如果__exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出。
异常抛出
#异常抛出,_exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出
class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, traceback):
self.file_obj.close()
print "type:",type
print "value:",value
print "traceback:",traceback
with File('demo.txt', 'w') as opened_file:
opened_file.undefined_function('Hola!')
#output================================================
# type: <type 'exceptions.AttributeError'>
# value: 'file' object has no attribute 'undefined_function'
# traceback: <traceback object at 0x000000000262D9C8>
# opened_file.undefined_function('Hola!')
# AttributeError: 'file' object has no attribute 'undefined_function'
异常忽略:
#异常忽略,__exit__返回的是True,那么这个异常就被忽略。
class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, exception_type, exception_value, traceback):
print("Exception has been handled")
self.file_obj.close()
return True
with File('demo.txt', 'w') as opened_file:
opened_file.undefined_function('Hola!')
# output==================================
# Exception has been handled
使用contextlib模块装饰器和生成器
2、方法二:contextlib模块装饰器和生成器实现
这种方式实现更优雅,我个人更喜欢这种方式。
yield之前的代码由__enter__方法执行,yield之后的代码由__exit__方法执行。本质上还是__enter__和__exit__方法。
# coding:utf-8
import contextlib
@contextlib.contextmanager
def myopen(filename, mode):
f = open(filename, mode)
try:
yield f.readlines()
except Exception as e:
print e
finally:
f.close()
if __name__ == '__main__':
with myopen(r'c:\ip2.txt', 'r') as f:
for line in f:
print line
单个with语句管理多个上下文
3、with语句上多个下文关联
直接通过一个with语句打开多个上下文,即可同时使用多个上下文变量,而不必需嵌套使用with语句。
class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, exception_type, exception_value, traceback):
self.file_obj.close()
return True
with File('demo.txt', 'w') as f1,File('demo.txt','w') as f2:
print f1,f2
# Output============================# <open file 'demo.txt', mode 'w' at 0x000000000263D150> <open file 'demo.txt', mode 'w' at 0x000000000263D1E0>
更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》
希望本文所述对大家Python程序设计有所帮助。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













