转自:http://blog.jkey.lu/2013/03/15/python-decorator-and-functools-module/

什么是装饰器?

在 python 语言里第一次看到装饰器不免让人想到设计模式中的装饰模式——动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

好吧,python 中的装饰器显然和装饰模式毫无关系。那 python 中的装饰器到底是什么呢?

简而言之,装饰器提供了一种方法,在函数和类定义语句的末尾插入自动运行代码。python 中有两种装饰器:函数装饰器和类装饰器。

函数装饰器

简单的装饰器例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def decorator(F): # 装饰器函数定义
print "I'm decorator"
return F @decorator
def foo():
print 'Hello World!'
# 上面等价于 foo = decorator(foo) foo()
"""
I'm decorator
Hello World!
""" decorator(foo)() # 所以这里的输出与 foo() 相同
"""
I'm decorator
Hello World!
"""

从上面运行后结果看出,装饰器就是一个能够返回可调用对象(函数)的可调用对象(函数)。

具有封闭作用域的装饰器

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
def decorator(func): # 装饰器函数
print 'in decorator'
def wrapper(*args):
print 'in decorator wrapper'
wrapper._calls += 1
print "calls = %d" % (wrapper._calls)
func(*args)
wrapper._calls = 0
return wrapper @decorator
def foo(x, y):
print "x = %d, y = %d" % (x, y) foo(1, 2) # 第一次调用
"""
in decorator
in decorator wrapper
calls = 1
x = 1, y = 2
""" foo(2, 3) # 第二次调用
"""
in decorator wrapper
calls = 2
x = 2, y = 3
"""

可以看出第一次调用 foo(1, 2) 时,相当于

1
2
foo = decorator(foo)
foo(1, 2)

第二次调用 foo(2, 3) 时 foo 已经为 decorator(foo) 的返回值了

再看看一个装饰器类来实现的:

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
class decorator: # 一个装饰器类
def __init__(self, func):
print 'in decorator __init__'
self.func = func
self.calls = 0
def __call__(self, *args):
print 'in decorator __call__'
self.calls += 1
print "calls = %d" % (self.calls)
self.func(*args) @decorator
def foo(x, y):
print "x = %d, y = %d" % (x, y) foo(1, 2) # 第一次调用
"""
in decorator __init__
in decorator __call__
calls = 1
x = 1, y = 2
""" foo(2, 3) # 第二次调用
"""
in decorator __call__
calls = 2
x = 2, y = 3
"""

装饰器参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def decorator_wrapper(a, b):
print 'in decorator_wrapper'
print "a = %d, b = %d" % (a, b)
def decorator(func):
print 'in decorator'
def wrapper(*args):
print 'in wrapper'
func(*args)
return wrapper
return decorator @decorator_wrapper(1, 2) # 这里先回执行 decorator_wrapper(1, 2), 返回 decorator 相当于 @decorator
def foo(word):
print word foo('Hello World!')
"""
in decorator_wrapper
a = 1, b = 2
in decorator
in wrapper
Hello World!
"""

functools 模块

functools 模块中有三个主要的函数 partial(), update_wrapper() 和 wraps(), 下面我们分别来看一下吧。

partial(func[,args][, *keywords])

看源码时发现这个函数不是用 python 写的,而是用 C 写的,但是帮助文档中给出了用 python 实现的代码,如下:

1
2
3
4
5
6
7
8
9
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc

OK,可能一下子没看明白,那么继续往下看,看一下是怎么用的。我们知道 python 中有个 int([x[,base]]) 函数,作用是把字符串转换为一个普通的整型。如果要把所有输入的二进制数转为整型,那么就要这样写 int('11', base=2)。这样写起来貌似不太方便,那么我们就能用 partial 来实现值传递一个参数就能转换二进制数转为整型的方法。

1
2
3
4
5
from functools import partial
int2 = partial(int, base=2) print int2('11') # 3
print int2('101') # 5

update_wrapper(wrapper, wrapped[, assigned][, updated])

看这个函数的源代码发现,它就是把被封装的函数的 modulenamedoc 和 dict 复制到封装的函数中去,源码如下,很简单的几句:

1
2
3
4
5
6
7
8
9
10
11
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
return wrapper

具体如何用我们可以往下看一下。

wraps(wrapped[, assigned][, updated])

wraps() 函数把用 partial() 把 update_wrapper() 给封装了一下。

1
2
3
4
5
6
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES): return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)

好,接下来看一下是如何使用的,这才恍然大悟,一直在很多开源项目的代码中看到如下使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from functools import wraps
def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
print 'Calling decorated function'
return f(*args, **kwds)
return wrapper @my_decorator
def example():
"""这里是文档注释"""
print 'Called example function' example() # 下面是输出
"""
Calling decorated function
Called example function
"""
print example.__name__ # 'example'
print example.__doc__ # '这里是文档注释'

python 装饰器和 functools 模块的更多相关文章

  1. python装饰器中functools.wraps的作用详解

    直接上代码看效果: # 定义一个最简单的装饰器 def user_login_data(f): def wrapper(*args, **kwargs): return f(*args, **kwar ...

  2. [转]python 装饰器

    以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,但是这样的话是不是还要大体读读 ...

  3. Python装饰器,Python闭包

    可参考:https://www.cnblogs.com/lianyingteng/p/7743876.html suqare(5)等价于power(2)(5):cube(5)等价于power(3)(5 ...

  4. [python 基础]python装饰器(一)添加functools获取原函数信息以及functools.partial分析

    python装饰器学习的时候有两点需要注意一下 1,被装饰器装饰的函数取其func.__name__和func.func_doc的时候得到的不是被修饰函数的相关信息而是装饰器wrapper函数的doc ...

  5. python 装饰器、递归原理、模块导入方式

    1.装饰器原理 def f1(arg): print '验证' arg() def func(): print ' #.将被调用函数封装到另外一个函数 func = f1(func) #.对原函数重新 ...

  6. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

  7. Python装饰器与面向切面编程

    今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...

  8. 【转】Python装饰器与面向切面编程

    原文请参考: http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html 今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切 ...

  9. Python第二十六天 python装饰器

    Python第二十六天 python装饰器 装饰器Python 2.4 开始提供了装饰器( decorator ),装饰器作为修改函数的一种便捷方式,为工程师编写程序提供了便利性和灵活性装饰器本质上就 ...

随机推荐

  1. linux概念之用户,组及权限

    Linux 是一个多用户.多任务的操作系统:我们应该了解单用户多任务和多用户多任务的概念 单用户多任务一个beinan用户,为了完成工作,执行了几个任务:当然beinan这个用户,其它的人还能以远程登 ...

  2. jQuery Mobile_事件

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  3. web首页设置如下代码可判断用户是用什么设备登录的?

    var OnePage=true;//用来判断staticHtml.js中首页登入的信息判断var _mobileUrl = "http://a.abc.com";//手机用户通过 ...

  4. Android ImageButton android:scaleType

    ImageView的属性android:scaleType,即 ImageView.setScaleType(ImageView.ScaleType). android:scaleType是控制图片如 ...

  5. 预定义宏__GNUC__和_MSC_VER

    一.预定义__GNUC__宏 1 __GNUC__ 是gcc编译器编译代码时预定义的一个宏.需要针对gcc编写代码时, 可以使用该宏进行条件编译. 2 __GNUC__ 的值表示gcc的版本.需要针对 ...

  6. erlang和java通信

    连接在 https://guts.me/2014/07/27/erlang-communicates-with-java/ 代码在 https://github.com/mingshun/jinter ...

  7. js常见数字处理整理

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...

  8. 偷懒小工具 - SSO单点登录通用类(可跨域)(上)

    目的  目的很明确,就是搭建单点登录的帮助类,并且是一贯的极简风格(调用方法保持5行以内). 并且与其他类库,关联性降低.所以,不使用WebAPI或者WebService等. 思路   因为上次有朋友 ...

  9. room_speed和image_speed

    room_speed是游戏步数,每秒多少步(步事件)image_speed是动画帧率room_speed变则整个游戏变慢image_speed变只是该object动画变慢 除了游戏全局加速减速,一般不 ...

  10. C# WinForm开发系列 - ListBox/ListView/Panel

    转自会飞的小猪文章 C# WinForm开发系列 - ListBox/ListView/Panel 在博客园看到了一篇博文,觉得很不错,就转载过来了.    包含自定义绘制的ListBox, 带拖动, ...