从with关键字到编写自己简单的ContextManager(一)

Python with语句解析
本文首先介绍了Python中with表达式的用途及其如何简化try-except-finally的异常处理过程,特别是对于文件操作。随后通过自定义类`ConMgr`演示了如何实现`__enter__`和`__exit__`方法来创建一个上下文管理器。此外,还探讨了Python `contextlib`模块的使用,包括`contextmanager`装饰器如何简化上下文管理器的创建。
本文先介绍with表达式,然后再试图用with以及装饰器等知识实现自己的ContextManager

with可以干什么?我的理解是简化try except finally的工作,比如打开文件操作符,读文件,捕捉异常,最后关闭。这个例子是with最最常用的方法了,满大街都可以找到这个例子。
除文件open操作之外,其实其它很多操作也可以掐头去尾,留下中间关键操作就行。
那么该如何实现呢?按照python文档解释,只要实现__enter__和__exit__两个函数就可以。
很简单:
class ConMgr(object):
def __init__(self):
print("__init__ called")


def __call__(self, *args):
print("__call__ called")
#: 可以把传进来的参数保存着,在with开始时运行
self.args = args
return self


def __enter__(self):
# 打印之前传进来的参数
print("__enter__ called", self.args)
return 'abcd'


def __exit__(self, exc_type, exc_val, exc_tb):
print("__exit__ called: exc_type = %s exc_val = %s exc_tb = %s "\
% (exc_type, exc_val, exc_tb))
return "exited"


def test1():
c = ConMgr()
with c('pppp') as tmp:
print(tmp)
print("haha")
assert 1>2
print(3)

test1()

输出结果是:
__init__ called
__call__ called
('__enter__ called', ('pppp',))
abcd
haha
__exit__ called: exc_type = <type 'exceptions.AssertionError'> exc_val = exc_tb = <traceback object at 0x7f911003b128>

首先请注意,这里的c()是类的一个实例,就是一个普通类,而不是generator。当c('pppp')执行时,调用了__call__函数,__call__函数赶紧把传入的参数保存了下来,等进了with块之后,调用__enter__之时再把参数放出来。
另外__call__函数一定要返回self,因为with块运行完了之后,将会调用self.__exit__()如果不返回self将找不到__exit__函数。
然后就是as语句的tmp值实际上是__enter__的返回值,返回什么都可以,无所谓的,哪怕传个闭包。这里的好主意是把之前的args可以传给tmp。
最后请仔细看,当assert 1>2发生错误之后,with块没有执行完就调用__exit__函数了。通过这个函数的参数,我们来实现异常处理。

接下来介绍下python的contextlib这个模块。
可能有朋友不知到,这个模块没有主轴功能,主要是围绕with语句,提供了一些方便的util函数操作。
这个模块里面有一个contextmanager的装饰器,它可以省掉我们之前那么麻烦创建一个class然后补上__enter__和__exit__的过程,它利用工厂模式生成一个generator,然后就可以方便的使用with语句了。
关于官方contextlib模块里面的功能,我想自己能不能做一个山寨版出来,详细见[url=http://luozhaoyu.iteye.com/blog/1512917]下文[/url]
Python 编程里,with 关键字是强大且常用的工具,借助上下文管理器简化资源管理,使代码更清晰、可读,还能避免资源泄漏和异常处理问题[^1]。 ### 使用方法 #### 基础用法 以文件操作为例,常见的用法是 `with open(file) as f`。如下是普通版文件操作与使用 with 关键字操作的对比: ```python # 普通版 file = "test.txt" def fun1(): f = open(file, "w") f.write("hello python") f.close() # 使用 with 关键字 with open(file, "w") as f: f.write("hello python") ``` 普通版需要手动调用 `close` 方法关闭文件,而使用 with 关键字,文件在代码块执行完毕后会自动关闭[^2]。 #### 自定义上下文管理器 使用 `contextmanager` 装饰器可以自定义上下文管理器。装饰器 `contextmanager` 被调用并返回个帮助方法,这个帮助函数在被调用后会生成个 `GeneratorContextManager` 实例。最终 with 表达式中的 `EXPR` 调用的是由 `contextmanager` 装饰器返回的帮助函数[^4]。示例代码如下: ```python from contextlib import contextmanager @contextmanager def my_context_manager(): # 进入上下文时的操作 print("Entering the context") try: yield finally: # 离开上下文时的操作 print("Exiting the context") with my_context_manager(): print("Inside the context") ``` ### 原理 with 关键字的工作依赖于上下文管理器。上下文管理器是实现了 `__enter__` 和 `__exit__` 方法的对象。当执行 `with` 语句时,首先调用上下文管理器的 `__enter__` 方法,该方法的返回值会赋给 `as` 后面的变量。当代码块执行完毕(无论是否发生异常),都会调用上下文管理器的 `__exit__` 方法进行资源清理。 ### 应用场景 - **文件操作**:如上述例子,确保文件在使用后被正确关闭。 - **数据库连接**:使用 with 关键字可以保证数据库连接在使用完后被关闭,避免资源泄漏。 - **线程锁**:在多线程编程中,使用 with 关键字可以确保锁在使用完后被释放。 ### 优势 - **自动资源释放**:保证资源在使用后被正确关闭。 - **代码简洁**:减少样板代码。 - **异常安全**:即便代码块中发生异常,资源也会被正确释放。 - **可读性强**:能明确标识资源的作用域[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值