咱们用的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
|