理解Python中的装饰器

来源:互联网 发布:ps制作淘宝店铺首页 编辑:程序博客网 时间:2024/04/30 00:27


前几天看到Stackoverflow上的一个提问。如果使用如下的代码:

@makebold@makeitalicdef say():   return "Hello"


打印出如下的输出:

<b><i>Hello<i></b>



给出的答案如下:

def makebold(fn):    def wrapped():        return "<b>" + fn() + "</b>"    return wrapped  def makeitalic(fn):    def wrapped():        return "<i>" + fn() + "</i>"    return wrapped  @makebold@makeitalicdef hello():    return "hello world"  print hello() ## 返回 <b><i>hello world</i></b>


想要做更多深入的了解python的装饰器。你可以参考两篇很好的中文博客文章:

1. Python装饰器学习:http://blog.csdn.net/thy38/archive/2009/08/21/4471421.aspx

2. Python装饰器与面向切面编程:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html

现在我们来看看如何从一些最基础的方式来理解Python的装饰器。英文讨论参考Here。

要理解python的装饰器,我们首先必须明白在Python中函数也是被视为对象。这一点很重要。先看一个例子:

def shout(word="yes") :    return word.capitalize()+" !"  print shout()# 输出 : 'Yes !'  # 作为一个对象,你可以把函数赋给任何其他对象变量   scream = shout  # 注意我们没有使用圆括号,因为我们不是在调用函数# 我们把函数shout赋给scream,也就是说你可以通过scream调用shout  print scream()# 输出 : 'Yes !'  # 还有,你可以删除旧的名字shout,但是你仍然可以通过scream来访问该函数  del shouttry :    print shout()except NameError, e :    print e    #输出 : "name 'shout' is not defined"  print scream()# 输出 : 'Yes !'


我们暂且把这个话题放旁边,我们先看看python另外一个很有意思的属性:可以在函数中定义函数:

def talk() :      # 你可以在talk中定义另外一个函数    def whisper(word="yes") :        return word.lower()+"...";      # ... 并且立马使用它      print whisper()  # 你每次调用'talk',定义在talk里面的whisper同样也会被调用talk()# 输出 :# yes...  # 但是"whisper" 不会单独存在:  try :    print whisper()except NameError, e :    print e    #输出 : "name 'whisper' is not defined"*


函数引用

从以上两个例子我们可以得出,函数既然作为一个对象,因此:

1. 其可以被赋给其他变量

2. 其可以被定义在另外一个函数内

这也就是说,函数可以返回一个函数,看下面的例子:

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()        # 这里你可以看到talk其实是一个函数对象:print talk#输出 : <function shout at 0xb7ea817c>  # 该对象由函数返回的其中一个对象:print talk()  # 或者你可以直接如下调用 :print getTalk("whisper")()#输出 : yes...


还有,既然可以返回一个函数,我们可以把它作为参数传递给函数:

def doSomethingBefore(func) :    print "I do something before then I call the function you gave me"    print func()  doSomethingBefore(scream)#输出 :#I do something before then I call the function you gave me#Yes !


这里你已经足够能理解装饰器了,其他它可被视为封装器。也就是说,它能够让你在装饰前后执行代码而无须改变函数本身内容。

手工装饰

那么如何进行手动装饰呢?

# 装饰器是一个函数,而其参数为另外一个函数def my_shiny_new_decorator(a_function_to_decorate) :      # 在内部定义了另外一个函数:一个封装器。    # 这个函数将原始函数进行封装,所以你可以在它之前或者之后执行一些代码    def the_wrapper_around_the_original_function() :          # 放一些你希望在真正函数执行前的一些代码        print "Before the function runs"          # 执行原始函数        a_function_to_decorate()          # 放一些你希望在原始函数执行后的一些代码        print "After the function runs"      #在此刻,"a_function_to_decrorate"还没有被执行,我们返回了创建的封装函数    #封装器包含了函数以及其前后执行的代码,其已经准备完毕    return the_wrapper_around_the_original_function  # 现在想象下,你创建了一个你永远也不远再次接触的函数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  # 好了,你可以封装它实现行为的扩展。可以简单的把它丢给装饰器# 装饰器将动态地把它和你要的代码封装起来,并且返回一个新的可用的函数。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时,实际调用却是a_stand_alone_function_decorated。实现也很简单,可以用my_shiny_new_decorator来给a_stand_alone_function重新赋值。

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)a_stand_alone_function()#输出 :#Before the function runs#I am a stand alone function, don't you dare modify me#After the function runs  # And guess what, that's EXACTLY what decorators do !


装饰器揭秘

前面的例子,我们可以使用装饰器的语法:

@my_shiny_new_decoratordef another_stand_alone_function() :    print "Leave me alone"  another_stand_alone_function()#输出 :#Before the function runs#Leave me alone#After the function runs


是的,就是这么简单。@decorator可以表示为:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)


装饰器仅仅是装饰器设计模型的一个python话变体。python中还有其他经典的设计模式来简化开发,比如Iterator。

当然你也可以累积装饰:

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()#outputs :#</''''''\># #tomatoes## --ham--# ~salad~#<\______/>


使用python装饰器语法:

23456789101112@bread@ingredientsdef sandwich(food="--ham--") :    print food  sandwich()#输出 :#</''''''\># #tomatoes## --ham--# ~salad~#<\______/>


装饰器的顺序很重要,需要注意:

@ingredients@breaddef strange_sandwich(food="--ham--") :    print food  strange_sandwich()#输出 :##tomatoes##</''''''\># --ham--#<\______/># ~salad~


最后回答前面提到的问题:

# 装饰器makebold用于转换为粗体def makebold(fn):    # 结果返回该函数    def wrapper():        # 插入一些执行前后的代码        return "<b>" + fn() + "</b>"    return wrapper  # 装饰器makeitalic用于转换为斜体def makeitalic(fn):    # 结果返回该函数    def wrapper():        # 插入一些执行前后的代码        return "<i>" + fn() + "</i>"    return wrapper  @makebold@makeitalicdef say():    return "hello"  print say() #输出: <b><i>hello</i></b>  # 等同于 def say():    return "hello"say = makebold(makeitalic(say))  print say() #输出: <b><i>hello</i></b>

转自:http://www.open-open.com/lib/view/open1374584644558.html

1、可爱的 Python: Decorator 简化元编程

http://www.ibm.com/developerworks/cn/linux/l-cpdecor.html

2、Python的装饰器

http://jnotes.googlecode.com/svn/trunk/Notes/NotesOnPythonLearning/Python_decorator.html

3、Python装饰器学习

http://blog.csdn.net/thy38/article/details/4471421

4、通过 Python 装饰器实现DRY(不重复代码)原则

http://www.oschina.net/translate/dry-principles-through-python-decorators

5、http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/

原文地址:http://hmw.iteye.com/blog/1510673