咱们用的os模块,读取文件的时候,其实他是含有__enter__ __exit__ 。 一个是with触发的时候,一个是退出的时候。
1
2
|
with file ( 'nima,' r') as f: print f.readline() |
那咱们自己再实现一个标准的可以with的类。 我个人写python的时候,喜欢针对一些需要有关闭逻辑的代码,构造成with的模式 。
1
2
3
4
5
6
7
8
9
10
|
#encoding:utf-8 class echo: def __enter__( self ): print 'enter' def __exit__( self , * args): print 'exit' with echo() as e: print 'nima' |
contextlib是个比with优美的东西,也是提供上下文机制的模块,它是通过Generator装饰器实现的,不再是采用__enter__和__exit__。contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from contextlib import contextmanager @contextmanager def make_context() : print 'enter' try : yield {} except RuntimeError, err : print 'error' , err finally : print 'exit' with make_context() as value : print value |
我这里再贴下我上次写的redis分布式锁代码中有关于contextlib的用法。其实乍一看,用了with和contextlib麻烦了,但是最少让你的主体代码更加鲜明了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
from contextlib import contextmanager from random import random DEFAULT_EXPIRES = 15 DEFAULT_RETRIES = 5 @contextmanager def dist_lock(key, client): key = 'lock_%s' % key try : _acquire_lock(key, client) yield finally : _release_lock(key, client) def _acquire_lock(key, client): for i in xrange ( 0 , DEFAULT_RETRIES): get_stored = client.get(key) if get_stored: sleep_time = (((i + 1 ) * random()) + 2 * * i) / 2.5 print 'Sleeipng for %s' % (sleep_time) time.sleep(sleep_time) else : stored = client. set (key, 1 ) client.expire(key,DEFAULT_EXPIRES) return raise Exception( 'Could not acquire lock for %s' % key) def _release_lock(key, client): client.delete(key) |
Context Manager API
一个上下文管理器通过with声明激活, 而且API包含两个方法。__enter__()方法运行执行流进入到with代码块内。他返回一个对象共上下文使用。当执行流离开with块时,__exit__()方法上下文管理器清除任何资源被使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Context( object ): def __init__( self ): print '__init__()' def __enter__( self ): print '__enter__()' return self def __exit__( self , exc_type, exc_val, exc_tb): print '__exit__()' with Context(): print 'Doing work in the context.' |
打印结果
1
2
3
4
|
__init__() __enter__() Doing work in the context. __exit__() |
执行上下文管理器时会调用__enter__离开时调用__exit__。
__enter__能返回任意对象,联合一个指定名字于with声明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class WithinContext( object ): def __init__( self , context): print 'WithinContext.__init__(%s)' % context def do_something( self ): print 'WithinContext.do_something()' def __del__( self ): print 'WithinContext.__del__' class Context( object ): def __init__( self ): print '__init__()' def __enter__( self ): print '__enter__()' return WithinContext( self ) def __exit__( self , exc_type, exc_val, exc_tb): print '__exit__()' with Context() as c: c.do_something() |
打印结果
1
2
3
4
5
6
|
__init__() __enter__() WithinContext.__init__(<__main__.Context object at 0x7f579d8e4890>) WithinContext.do_something() __exit__() WithinContext.__del__ |
如果上下文管理器能处理异常,__exit__()应该返回一个True值表明这个异常不需要传播,返回False异常会在执行__exit__之后被引起。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Context( object ): def __init__( self , handle_error): print '__init__(%s)' % handle_error self .handle_error = handle_error def __enter__( self ): print '__enter__()' return self def __exit__( self , exc_type, exc_val, exc_tb): print '__exit__(%s, %s, %s)' % (exc_type, exc_val, exc_tb) return self .handle_error with Context( True ): raise RuntimeError( 'error message handled' ) print with Context( False ): raise RuntimeError( 'error message propagated' ) |
打印结果
1
2
3
4
5
6
7
8
9
10
11
|
__init__(True) __enter__() __exit__(<type 'exceptions.RuntimeError'>, error message handled, <traceback object at 0x7fdfb32f8b00>) __init__(False) __enter__() __exit__(<type 'exceptions.RuntimeError'>, error message propagated, <traceback object at 0x7fdfb32f8b90>) Traceback (most recent call last): File "test.py", line 23, in <module> raise RuntimeError('error message propagated') RuntimeError: error message propagated |
从生成器到上下文管理器
创建上下文管理的传统方法,通过编写一个类与__enter__()和__exit__()方法,并不困难。但有时比你需要的开销只是管理一个微不足道的上下文。在这类情况下,您可以使用contextmanager() decorat or 生成器函数转换成一个上下文管理器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
import contextlib @contextlib .contextmanager def make_context(): print ' entering' try : yield {} except RuntimeError, err: print ' Error:' , err finally : print ' exiting' print 'Normal:' with make_context() as value: print ' inside with statement:' , value print print 'handled ereor:' with make_context() as value: raise RuntimeError( 'show example of handling an error' ) print print 'unhandled error:' with make_context() as value: raise ValueError( 'this exception is not handled' ) |
打印结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Normal: entering inside with statement: {} exiting handled ereor: entering Error: show example of handling an error exiting unhandled error: entering exiting Traceback (most recent call last): File "test.py", line 30, in <module> raise ValueError('this exception is not handled') ValueError: this exception is not handled |
嵌套上下文
使用nested()可以同时管理多个上下文。
1
2
3
4
5
6
7
8
9
10
|
import contextlib @contextlib .contextmanager def make_context(name): print 'entering:' , name yield name print 'exiting:' , name with contextlib.nested(make_context( 'A' ), make_context( 'B' ), make_context( 'C' )) as (A, B, C): print 'inside with statement:' , A, B, C |
打印结果
1
2
3
4
5
6
7
|
entering: A entering: B entering: C inside with statement: A B C exiting: C exiting: B exiting: A |
因为Python 2.7和以后的版本不赞成使用nested(),因为可以直接嵌套
1
2
3
4
5
6
7
8
9
10
|
import contextlib @contextlib .contextmanager def make_context(name): print 'entering:' , name yield name print 'exiting:' , name with make_context( 'A' ) as A, make_context( 'B' ) as B, make_context( 'C' ) as C: print 'inside with statement:' , A, B, C |
关闭open的句柄
文件类支持上下文管理器, 但是有一些对象不支持。还有一些类使用close()方法但是不支持上下文管理器。我们使用closing()来为他创建一个上下文管理器。(类必须有close方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import contextlib class Door( object ): def __init__( self ): print ' __init__()' def close( self ): print ' close()' print 'Normal Example:' with contextlib.closing(Door()) as door: print ' inside with statement' print print 'Error handling example:' try : with contextlib.closing(Door()) as door: print ' raising from inside with statement' raise RuntimeError( 'error message' ) except Exception, err: print ' Had an error:' , err |
打印结果
1
2
3
4
5
6
7
8
9
10
|
Normal Example: __init__() inside with statement close() Error handling example: __init__() raising from inside with statement close() Had an error: error message |