在自己好好总结并对Python装饰器的执行过程进行分解之前,对于装饰器虽然理解它的基本工作方式,但对于存在复杂参数的装饰器(装饰器和函数本身都有参数),总是会感到很模糊,即使这会弄懂了,下一次也很快忘记,其实本质上还是没有多花时间去搞懂其中的细节问题。
虽然网络上已经有很多这样的文章,但显然都是别人的思想,因此自己总是记不牢,所以花点时间自己好好整理一下。
最近在对《Python核心编程》做总结,收获了不少,下面分享一下我自己对于Python装饰器的理解,后面还提供了一个较为复杂的Python装饰器的执行过程的分解,可以参考一下。
1.Python装饰器的出现
在没有装饰器之前,如果要在类中定义一个静态方法,需要使用下面的方法:
1
2
3
|
class
MyClass(
object
):
def
staticFoo():
staticFoo
=
staticmethod
(staticFoo)
|
即要在该静态方法中加入类似staticmethod()内建函数将该方法转换为静态方法,这显然非常麻烦,而有了装饰器之后,就可以写成下面这样:
1
2
3
4
|
class
MyClass(
object
):
@
staticmethod
def
staticFoo():
pass
|
这样就简洁很多了。
2.Python装饰器类型与理解
(1)无参数装饰器
-
一个装饰器
下面的情况:
1
2
3
|
@f
def
foo():
pass
|
其实就相当于:
1
2
3
|
def
foo():
pass
foo
=
g(foo)
|
-
多个装饰器
下面的情况:
1
2
3
4
|
@g
@f
def
foo():
pass
|
就相当于:
1
2
3
|
def
foo():
pass
foo
=
g(f(foo))
|
(2)含参数装饰器
-
带有参数的一个装饰器
下面的情况:
1
2
3
|
@decomaker
(deco_args)
def
foo():
pass
|
就相当于:
1
2
3
|
def
foo():
pass
foo
=
decomaker(deco_args)(foo)
|
用这样的思想去理解就非常好理解了:decomaker()用deco_args做了些事并返回函数对象,而该函数对象正是以foo作为其参数的装饰器。
下面多个装饰器的例子也是按这样的思想去理解。
-
带有参数的多个装饰器
下面的情况:
1
2
3
4
|
@deco1
(deco_arg)
@deco2
()
def
foo():
pass
|
就相当于:
1
2
3
|
def
foo():
pass
foo
=
deco1(deco_arg)(deco2(foo))
|
3.Python装饰器执行过程的手动分解
OK,有了上面的理论基础,理解下面一个较为复杂的装饰器就很容易了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from
functools
import
wraps
def
log(text):
def
decorator(func):
@wraps(func)
#it works like:wraper.__name__ = func.__name__
def
wrapper(
*
args,
*
*
kwargs):
print
'%s %s():'
%
(text, func.__name__)
return
func(
*
args,
*
*
kwargs)
return
wrapper
return
decorator
@log
(
'Hello'
)
def
now(area):
print
area,
'2016-01-23'
now(
'Beijing'
)
print
'The name of function now() is:'
, now.__name__
|
执行如下:
1
2
3
4
|
/
usr
/
bin
/
python2.
7
/
home
/
xpleaf
/
PycharmProjects
/
decorator_test
/
dec10.py
Hello now():
Beijing
2016
-
01
-
23
The name of function now()
is
: now
|
对于该程序的执行过程,可以分析如下: 1.先执行log('Hello')函数,此时返回了一个新的函数,只不过其中的text变量被替换为'Hello',所以用来装饰now函数的新的装饰器如下:
2.所以此时的now函数,就相当于:
3.即now就相当于:
所以,输出的结果也就非常好理解了。 |
关于wraps,它也是一个装饰器,使用它的作用是,被我们用自定义装饰器修改后的函数,它的函数名称,即func.__name__跟原来是一样的,而它的工作原理正如上面所提及的,即:
1
|
wraper.__name__
=
func.__name__
|
也就是说,使用wraps可以不改变原来函数的属性,当然,上面只是简单说明了一下其工作原理,详细的可以参考wraps的源代码。
本文转自 xpleaf 51CTO博客,原文链接:http://blog.51cto.com/xpleaf/1763567,如需转载请自行联系原作者