最新消息:

python中decorator详解(转)

Python 大步 732浏览 0评论

转载请注明来源:唐磊的个人博客《python中decorator详解》

前面写python的AOP解决方案时提到了decorator,这篇文章就详细的来整理下python的装饰器——decorator。

python中的函数即objects

一步一步来,先了解下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
def shout(word='hello,world'):
    return word.capitalize() + '!'

print shout()
#输出:Hello,world!
#跟其他对象一样,你同样可以将函数对象赋值给其他变量
scream = shout
#注意,shout后面没有用括号(),用括号就是调用了shout方法,这里仅仅是将shout的函数对象复制给scream。
#意味着,你可以通过scream调用shout方法。
print scream()
#同样输出:Hello,world!
#此外,你将代表函数变量的shout删除,shout现在失效,但scream同样可以调用

del shout
print shout()
#会抛异常
#Traceback (most recent call last):
#  File "S:decorator.py", line 18, in <module>
#    print shout()
#NameError: name 'shout' is not defined

#(当然得注释掉前面的两句代码,或者try catch一下)
print scream()
#同样输出:Hello,world!

python中函数还可以嵌套,即将函数定义在另一个函数里面。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def talk():
   
    def whisper(word='hello'):
        return word.lower() + '...'

    #在函数talk里面定义whisper函数,马上就可以用了
    print whisper()
#调用talk,里面的whisper()也被调用了
talk()
#输出:hello...
#直接调用whisper可不行。
try:
    print whisper()
except Exception ,e :
    print e
#输出:name 'whisper' is not defined

从上面的例子我们已经可以得到,函数可以被赋值给另外的变量,也能在另一个函数里面定义函数。可以推断函数也能返回给另外的函数或者作为参数传递给其他函数。看下面的代码:

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='hello,world'):
        return word.capitalize() + '!'
    def whisper(word='hello'):
        return word.lower() + '...'

    #根据传入的参数,返回相应的函数对象
    #注意,没用(),仅仅返回函数对象,并没有调用
    if type == 'shout':
        return shout
    else:
        return whisper

talk = getTalk()
print talk
#输出:<function shout at 0x011FCA70>
print talk() #调用函数
#输出:Hello,world!
#同样可以这样直接调用
print getTalk('whisper')()
#输出:hello...

#既然能return一个函数,当然也可以将函数作为参数传递给其他的函数
def doBefore(func):
    print 'before calling the func'
    print func()#调用

doBefore(talk)#将前面通过getTalk()得到的函数对象传递给doBefore
#输出:
#before calling the func
#Hello,world!

 

初识decorator

明白以上的这些,将有助于理解装饰器decorator,其实装饰器decorator就是在不改变函数的本身情况下在函数执行的前后包装另外的代码来改变函数的具体表现行为。有点儿AOP的味道。继续从代码来解释。

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
# 装饰器就是一个将其他函数(也就是被装饰的函数)作为参数的一个function
def my_shiny_new_decorator(a_function_to_decorate):

    # 在这个装饰器函数里面用另外一个函数来包装原函数,
    # 即在调用原来的函数前后分别执行其他的操作.
    def the_wrapper_around_the_original_function():

        # 这里放一些你希望在原来的函数执行之前的代码,例如log、身份验证等
        print "Before the function runs"

        # 调用原函数本身,注意有()
        a_function_to_decorate()

        #同理,这里放一些你希望在原来的函数执行之后的代码,例如释放资源等
        print "After the function runs"

    # 注意,此时,函数 "a_function_to_decorate"并没有被执行.
    # 此时将刚刚创建的用来包装原函数的函数返回
    return the_wrapper_around_the_original_function

# 假设你定义了一个函数,并良好实现,也觉得perfect了。且发誓以后并不准备改动这段代码
def a_stand_alone_function():
    print "I am a stand alone function, don't you dare modify me"

a_stand_alone_function()
#输出: I am a stand alone function, don't you dare modify me

# 有时候,作为coder,并不是你不动代码就能不动的。PM此时另外又来了个需求,让你在之前
#的基础上再加点XX功能,苦逼的程序猿乖乖的改吧。不过有了装饰器,你能满足PM的需求,又能不违背
#当初许下的不改变原有代码的誓言。仅仅需要将此函数拿去重新装饰一下即可。如下:
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#输出:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

如果不想调用a_stand_alone_function_decorated()这个方法,还是钟情于以前的那个名字,方法也简单。重写下a_stand_alone_function方法即可。即:

1
2
3
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#输出还是一样样

decorator揭秘

上面的代码就是装饰器的体现。现在用python中专门的decorator语法就是酱紫滴:

1
2
3
4
5
6
7
8
@my_shiny_new_decorator
def another_stand_alone_function():
    print "Leave me alone"

another_stand_alone_function()
#Before the function runs
#Leave me alone
#After the function runs

简单来说,@my_shiny_new_decorator就是another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)的缩写。

python中的装饰器其实是GoF中的装饰模式的一个变种。像迭代器iterators也是一种设计模式。

装饰器还可以同时使用多个。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def bread(func):
    def wrapper():
        print "</''''''>"
        func()
        print "<______/>"
    return wrapper

def ingredients(func):
    def wrapper():
        print "#tomatoes#"
        func()
        print "~salad~"
    return wrapper

def sandwich(food="--ham--"):
    print food

sandwich()
#输出: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#输出:
#</''''''>
# #tomatoes#
# --ham--
# ~salad~
#<______/>

#用python中的decorator语法就是:
@bread
@ingredients
def sandwich(food="--ham--"):
    print food

sandwich()
#输出:
#</''''''>
# #tomatoes#
# --ham--
# ~salad~
#<______/>
#注意@的顺序。谁在最下面,睡最先被调用。
#下面的sandwich就奇怪了哈,把salad等放在盘子底下
@ingredients
@bread
def strange_sandwich(food="--ham--"):
    print food

strange_sandwich()
#输出:
##tomatoes#
#</''''''>
# --ham--
#<______/>
# ~salad~

被装饰的函数传参

仅仅在包装的函数上将参数传递过来即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def a_decorator_passing_arguments(function_to_decorate):
    def a_wrapper_accepting_arguments(arg1, arg2):
        print "传进来的参数是:", arg1, arg2
        function_to_decorate(arg1, arg2)
    return a_wrapper_accepting_arguments

# 既然你调用了装饰过后的函数,你也就调用了包装器,函数也就自然传递到了包装器。

@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
    print "My name is", first_name, last_name

print_full_name("lei", "tang")
# 输出:
#传进来的参数是: lei tang
#My name is lei tang

装饰类中的方法

python中的方法和函数差不多,只不过类中的

转载请注明:大步's Blog » python中decorator详解(转)

SiteMap