最近在写 Python+Django 的时候发现,有时候封装 API 的时候经常会遗失一些重复的装饰信息,但是直接封装到方法里是比较差劲的写法,因为有多个模块可能同时需要这些装饰信息,所以我希望使用一种可以迭代的装饰器。于是我在 Stack Overflow 上找到了相应的解答。下面以这篇解答为引写下我理解 Python decorator 的思路过程。
装饰器是做什么用的?
装饰器实现对一个已有的模块做一些“修饰工作”,所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去。
装饰器的定义
首先,你需要知道 Python 的闭包,接着发现3点 Python 的特性在装饰器中运用:
- 函数可以赋值给一个变量。
- 函数可以定义在另一个函数内部。
- 函数名可以作为函数返回值。
辣么,先来看一段代码:
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
|
def getTalk(type="shout"):
# 定义函数
def shout(word="yes"):
return word.capitalize()+"!"
def whisper(word="yes") :
return word.lower()+"...";
# 返回函数
if type == "shout":
# 没有使用"()", 并不是要调用函数,而是要返回函数对象
return shout
else:
return whisper
# 如何使用?
# 将函数返回值赋值给一个变量
talk = getTalk()
# 我们可以打印下这个函数对象
print talk
#outputs : <function shout at 0xb7ea817c>
# 这个对象是函数的返回值
print talk()
#outputs : Yes!
# 不仅如此,你还可以直接使用之
print getTalk("whisper")()
#outputs : yes...
|
既然函数可以作为返回值,是不是函数也可以作为参数传递呢
1
2
3
4
5
6
7
8
|
def doSomethingBefore(func):
print "I do something before then I call the function you gave me"
print func()
doSomethingBefore(scream)
#outputs:
#I do something before then I call the function you gave me
#Yes!
|
所以看过这两段代码,你一定明白了,装饰器的定义。
装饰器就是封装器,可以让你在被装饰函数之前或之后执行代码,而不必修改函数本身代码。
怎么写封装器:
首先,我们来手写一个封装器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def new_decorator(func):
def wrapper():
print("before the function runs")
func()
print("after the function runs")
return wrapper
def along_func():
print("I am a alone function")
decorated_along_func = new_decorator(along_func)
decorated_along_func()
#outputs:
#before the function runs
#I am a alone function
#after the function runs
|
这里每次调用 decorated_along_func 函数时,都会将 along_func 函数传入到装饰函数 new_decorator 中,完成封装。
怎么写装饰器:
那将上例代码稍微进行修改:
1
2
3
4
5
6
7
8
9
10
11
12
|
def new_decorator(func):
def wrapper():
print("before the function runs")
func()
print("after the function runs")
return wrapper
@new_decorator
def along_func():
print("I am a alone function")
along_func()
|
就会发现会得到相同的结果,这就是装饰器!
那么回到我最初的问题,装饰器能否迭代呢?
可以!
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
|
def decorator1(func):
def wrapper():
print("before the function runs")
func()
print("after the function runs")
return wrapper
def decorator2(func):
def wrapper():
print("before the decorator1 runs")
func()
print("after the decorator1 runs")
return wrapper
@decorator2
@decorator1
def along_func():
print("I am a alone function")
along_func()
#outpus:
#before the decorator1 runs
#before the function runs
#I am a alone function
#after the function runs
#after the decorator1 runs
|
这种特性十分的便捷,但是必须注意装饰器的顺序。
如果上例代码写成:
1
2
3
4
|
@decorator1
@decorator2
def along_func():
print("I am a alone function")
|
那么结果将变为
1
2
3
4
5
|
before the function runs
before the decorator1 runs
I am a alone function
after the decorator1 runs
after the function runs
|
一些迭代装饰器的用法
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
|
# bold装饰器
def makebold(fn):
def wrapper():
# 在前后加入标签
return "<b>" + fn() + "</b>"
return wrapper
# italic装饰器
def makeitalic(fn):
def wrapper():
# 加入标签
return "<i>" + fn() + "</i>"
return wrapper
@makebold
@makeitalic
def say():
return "hello"
print say()
#outputs: <b><i>hello</i></b>
# 等价的代码
def say():
return "hello"
say = makebold(makeitalic(say))
print say()
#outputs: <b><i>hello</i></b>
|
是不是灰常炫酷。
高级用法
关于更多装饰器的高级用法,你可以戳以下链接:
戳我
关于 Python Decroator 的各种提案,可以参看:
Python Decorator Proposals