Python的装饰器和with语法

1
2
<python 2.7>
本文分两部分,第一部分是装饰器,第二部分是with语法,
1
2
3
4
5
@with_connection
def do_some_db_operation():
db.select('...')
db.update('...')
db.update('...')
1
2
3
4
with db.connection():
db.select('...')
db.update('...')
db.update('...')

上下两个代码片的作用是一样的,关于数据库操作的,都使用了with语法,只不过上边的使用了装饰器,更加方便了。

<装饰器>

动态增加代码功能,定义一个who的装饰器:

1
2
3
4
5
6
7
8
import functools

def who(func):
@functools.wraps(func)
def wrapper(*args, **kw):
增加的功能'x'代码,比如 print '<我是小菜>'
return func(*args, **kw)
return wrapper

使用时:

1
2
3
@who
def beatDoudou():
print '打豆豆'

结果:

1
2
<我是小菜>
打豆豆

关于@functools.wraps(fund)的作用是:
因为在原函数外加了一层wrapper,所以调用beatDoudou.__name__时,返回的是wrapper,并不是beatDoudou,避免有些依赖函数签名的代码执行出错。
带参数的装饰器

1
2
3
4
5
6
7
8
9
10
import functools

def who(name):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print '<我是%s>' % name
return func(*args, **kw)
return wrapper
return decorator

使用时:

1
2
3
@who
def beatDoudou('大鸟'):
print '打豆豆'

结果:

1
2
<我是大鸟>
打豆豆
1
2
3
4
5

<with 语法>
---------

使用with语法,首先要有上下文管理器(class),这个上下文管理器遵守上下文管理协议,即实现了__enter__() 和 __exit__() 方法,比如(代码看看格式就好):
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
32
33
34
35
36
37
38
39
40
41

class _ConnectionCtx(object):
"""
with connection():
pass
with connection():
pass
"""
def __enter__(self):
"""
获得一个惰性连接对象
"""
global _db_ctx
self.should_cleanup = False
if not _db_ctx.is_init():
_db_ctx.init()
self.should_cleanup = True
return self
def __exit__(self, exctype, excvalue, traceback):
"""
释放连接对象
"""
global _db_ctx
if self.should_cleanup:
_db_ctx.cleanup()
#with _ConnectionCtx():关键句
def with_connection(func):
"""
设计一个装饰器,替换with语法,让代码更优雅
比如:
@with_connection
def foo(*args, **kw):
f1()
f2()
f3()
"""
@functools.wraps(func)
def _wrapper(*args, **kw):
with _ConnectionCtx():
return func(*args, **kw)
return _wrapper

with语法作用

1
2
3
4
with db.connection():
db.select('...')
db.update('...')
db.update('...')

三个数据库操作成为语句块,在执行之前,先执行上下文管理器的enter函数,即获得数据库连接;在执行完语句块之后,再执行exit函数,即释放数据库连接。

with语句执行过程类似如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
以下代码来自[这里](https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/)
context_manager = context_expression
exit = type(context_manager).__exit__
value = type(context_manager).__enter__(context_manager)
exc = True # True 表示正常执行,即便有异常也忽略;False 表示重新抛出异常,需要对异常进行处理
try:
try:
target = value # 如果使用了 as 子句
with-body # 执行 with-body
except:
# 执行过程中有异常发生
exc = False
# 如果 __exit__ 返回 True,则异常被忽略;如果返回 False,则重新抛出异常
# 由外层代码对异常进行处理
if not exit(context_manager, *sys.exc_info()):
raise
finally:
# 正常退出,或者通过 statement-body 中的 break/continue/return 语句退出
# 或者忽略异常退出
if exc:
exit(context_manager, None, None, None)
# 缺省返回 None,None 在布尔上下文中看做是 False

ps:markdown编辑器有bug吧,有些内容显示不出来,只好改成代码片才可以,所以排版有点乱,sorry

显示 Gitment 评论