怎么作才能让一个函数同时用两个装饰器,像下面这样:html
@makebold @makeitalic def say(): return "Hello"
我但愿获得python
<b><i>Hello</i></b>
我只是想知道装饰器怎么工做的!git
去看看文档,答在下面:设计模式
def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makebold @makeitalic def hello(): return "hello world" print hello() ## returns <b><i>hello world</i></b>
若是你不想看详细的解释的话请看上面那个答案.api
要了解装饰器,你必须了解Python中的函数都是对象.这个意义很是重要.让咱们看看一个简单例子:缓存
def shout(word="yes"): return word.capitalize()+"!" print shout() # 输出 : 'Yes!' # 做为一个对象,你能够把它赋值给任何变量 scream = shout # 注意啦咱们没有加括号,咱们并非调用这个函数,咱们只是把函数"shout"放在了变量"scream"里. # 也就是说咱们能够经过"scream"调用"shout": print scream() # 输出 : 'Yes!' # 你能够删除旧名"shout",并且"scream"依然指向函数 del shout try: print shout() except NameError, e: print e #输出: "name 'shout' is not defined" print scream() # 输出: 'Yes!'
好了,先记住上面的,一会还会用到.app
Python函数另外一个有趣的特性就是你能够在一个函数里定义另外一个函数!dom
def talk(): # 你能够在"talk"里定义另外一个函数 ... def whisper(word="yes"): return word.lower()+"..." # 让咱们用用它! print whisper() # 每次调用"talk"时都会定义一次"whisper",而后"talk"会调用"whisper" talk() # 输出: # "yes..." # 可是在"talk"意外"whisper"是不存在的: try: print whisper() except NameError, e: print e #输出 : "name 'whisper' is not defined"*
好,终于到了有趣的地方了...异步
已经知道函数就是对象.所以,对象:函数
这就意味着函数能够返回另外一个函数.来看看!☺
def getTalk(kind="shout"): # 在函数里定义一个函数 def shout(word="yes"): return word.capitalize()+"!" def whisper(word="yes") : return word.lower()+"..."; # 返回一个函数 if kind == "shout": # 这里不用"()",咱们不是要调用函数 # 只是返回函数对象 return shout else: return whisper # 怎么用这个特性呢? # 把函数赋值给变量 talk = getTalk() # 能够看到"talk"是一个函数对象 print talk # 输出 : <function shout at 0xb7ea817c> # 函数返回的是对象: print talk() # 输出 : Yes! # 不嫌麻烦你也能够这么用 print getTalk("whisper")() # 输出 : yes...
既然能够return
一个函数, 你也能够在把函数做为参数传递:
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!
学习装饰器的基本知识都在上面了.装饰器就是"wrappers",它可让你在你装饰函数以前或以后执行程序,而不用修改函数自己.
怎么样本身作呢:
# 装饰器就是把其余函数做为参数的函数 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_decorate" 函数永远不会被执行 # 在这里返回刚才包装过的函数 # 在包装函数里包含要在原始函数先后执行的代码. 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() #输出s: #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
,很简单,只须要用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 # 想到了吗,这就是装饰器干的事!
用上一个例子,看看装饰器的语法:
@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
就这么简单.@decorator
就是下面的简写:
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
装饰器就是 decorator design pattern的pythonic的变种.在Python中有许多经典的设计模式来知足开发者.
固然,你也能够本身写装饰器:
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() #outputs: --ham-- sandwich = bread(ingredients(sandwich)) sandwich() #outputs: #</''''''\> # #tomatoes# # --ham-- # ~salad~ #<\______/>
用Python装饰器语法糖:
@bread @ingredients def sandwich(food="--ham--"): print food sandwich() #outputs: #</''''''\> # #tomatoes# # --ham-- # ~salad~ #<\______/>
改变一下顺序:
@ingredients @bread def strange_sandwich(food="--ham--"): print food strange_sandwich() #outputs: ##tomatoes# #</''''''\> # --ham-- #<\______/> # ~salad~
做为结论,相信你如今已经知道答案了:
# 字体变粗装饰器 def makebold(fn): # 装饰器将返回新的函数 def wrapper(): # 在以前或者以后插入新的代码 return "<b>" + fn() + "</b>" return wrapper # 斜体装饰器 def makeitalic(fn): # 装饰器将返回新的函数 def wrapper(): # 在以前或者以后插入新的代码 return "<i>" + fn() + "</i>" return wrapper @makebold @makeitalic def 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>
别轻松太早,看看下面的高级用法
# 这不是什么黑魔法,你只须要让包装器传递参数: def a_decorator_passing_arguments(function_to_decorate): def a_wrapper_accepting_arguments(arg1, arg2): print "I got args! Look:", 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("Peter", "Venkman") # 输出: #I got args! Look: Peter Venkman #My name is Peter Venkman
在Python里方法和函数几乎同样.惟一的区别就是方法的第一个参数是一个当前对象的(self
)
也就是说你能够用一样的方式来装饰方法!只要记得把self
加进去:
def method_friendly_decorator(method_to_decorate): def wrapper(self, lie): lie = lie - 3 # 女性福音 :-) return method_to_decorate(self, lie) return wrapper class Lucy(object): def __init__(self): self.age = 32 @method_friendly_decorator def sayYourAge(self, lie): print "I am %s, what did you think?" % (self.age + lie) l = Lucy() l.sayYourAge(-3) #输出: I am 26, what did you think?
若是你想造一个更通用的能够同时知足方法和函数的装饰器,用*args,**kwargs
就能够了
def a_decorator_passing_arbitrary_arguments(function_to_decorate): # 包装器接受全部参数 def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs): print "Do I have args?:" print args print kwargs # 如今把*args,**kwargs解包 # 若是你不明白什么是解包的话,请查阅: # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/ function_to_decorate(*args, **kwargs) return a_wrapper_accepting_arbitrary_arguments @a_decorator_passing_arbitrary_arguments def function_with_no_argument(): print "Python is cool, no argument here." function_with_no_argument() #输出 #Do I have args?: #() #{} #Python is cool, no argument here. @a_decorator_passing_arbitrary_arguments def function_with_arguments(a, b, c): print a, b, c function_with_arguments(1,2,3) #输出 #Do I have args?: #(1, 2, 3) #{} #1 2 3 @a_decorator_passing_arbitrary_arguments def function_with_named_arguments(a, b, c, platypus="Why not ?"): print "Do %s, %s and %s like platypus? %s" %\ (a, b, c, platypus) function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!") #输出 #Do I have args ? : #('Bill', 'Linus', 'Steve') #{'platypus': 'Indeed!'} #Do Bill, Linus and Steve like platypus? Indeed! class Mary(object): def __init__(self): self.age = 31 @a_decorator_passing_arbitrary_arguments def sayYourAge(self, lie=-3): # 能够加入一个默认值 print "I am %s, what did you think ?" % (self.age + lie) m = Mary() m.sayYourAge() #输出 # Do I have args?: #(<__main__.Mary object at 0xb7d303ac>,) #{} #I am 28, what did you think?
好了,如何把参数传递给装饰器本身?
由于装饰器必须接收一个函数当作参数,因此有点麻烦.好吧,你不能够直接把被装饰函数的参数传递给装饰器.
在咱们考虑这个问题时,让咱们从新回顾下:
# 装饰器就是一个'日常不过'的函数 def my_decorator(func): print "I am an ordinary function" def wrapper(): print "I am function returned by the decorator" func() return wrapper # 所以你能够不用"@"也能够调用他 def lazy_function(): print "zzzzzzzz" decorated_function = my_decorator(lazy_function) #输出: I am an ordinary function # 之因此输出 "I am an ordinary function"是由于你调用了函数, # 并不是什么魔法. @my_decorator def lazy_function(): print "zzzzzzzz" #输出: I am an ordinary function
看见了吗,和"my_decorator
"同样只是被调用.因此当你用@my_decorator
你只是告诉Python去掉用被变量my_decorator
标记的函数.
这很是重要!你的标记能直接指向装饰器.
让咱们作点邪恶的事.☺
def decorator_maker(): print "I make decorators! I am executed only once: "+\ "when you make me create a decorator." def my_decorator(func): print "I am a decorator! I am executed only when you decorate a function." def wrapped(): print ("I am the wrapper around the decorated function. " "I am called when you call the decorated function. " "As the wrapper, I return the RESULT of the decorated function.") return func() print "As the decorator, I return the wrapped function." return wrapped print "As a decorator maker, I return a decorator" return my_decorator # 让咱们建一个装饰器.它只是一个新函数. new_decorator = decorator_maker() #输出: #I make decorators! I am executed only once: when you make me create a decorator. #As a decorator maker, I return a decorator # 下面来装饰一个函数 def decorated_function(): print "I am the decorated function." decorated_function = new_decorator(decorated_function) #输出: #I am a decorator! I am executed only when you decorate a function. #As the decorator, I return the wrapped function # Let’s call the function: decorated_function() #输出: #I am the wrapper around the decorated function. I am called when you call the decorated function. #As the wrapper, I return the RESULT of the decorated function. #I am the decorated function.
一点都不难把.
下面让咱们去掉全部可恶的中间变量:
def decorated_function(): print "I am the decorated function." decorated_function = decorator_maker()(decorated_function) #输出: #I make decorators! I am executed only once: when you make me create a decorator. #As a decorator maker, I return a decorator #I am a decorator! I am executed only when you decorate a function. #As the decorator, I return the wrapped function. # 最后: decorated_function() #输出: #I am the wrapper around the decorated function. I am called when you call the decorated function. #As the wrapper, I return the RESULT of the decorated function. #I am the decorated function.
让咱们简化一下:
@decorator_maker() def decorated_function(): print "I am the decorated function." #输出: #I make decorators! I am executed only once: when you make me create a decorator. #As a decorator maker, I return a decorator #I am a decorator! I am executed only when you decorate a function. #As the decorator, I return the wrapped function. #最终: decorated_function() #输出: #I am the wrapper around the decorated function. I am called when you call the decorated function. #As the wrapper, I return the RESULT of the decorated function. #I am the decorated function.
看到了吗?咱们用一个函数调用"@
"语法!:-)
因此让咱们回到装饰器的.若是咱们在函数运行过程当中动态生成装饰器,咱们是否是能够把参数传递给函数?
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2): print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2 def my_decorator(func): # 这里传递参数的能力是借鉴了 closures. # 若是对closures感到困惑能够看看下面这个: # http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2 # 不要忘了装饰器参数和函数参数! def wrapped(function_arg1, function_arg2) : print ("I am the wrapper around the decorated function.\n" "I can access all the variables\n" "\t- from the decorator: {0} {1}\n" "\t- from the function call: {2} {3}\n" "Then I can pass them to the decorated function" .format(decorator_arg1, decorator_arg2, function_arg1, function_arg2)) return func(function_arg1, function_arg2) return wrapped return my_decorator @decorator_maker_with_arguments("Leonard", "Sheldon") def decorated_function_with_arguments(function_arg1, function_arg2): print ("I am the decorated function and only knows about my arguments: {0}" " {1}".format(function_arg1, function_arg2)) decorated_function_with_arguments("Rajesh", "Howard") #输出: #I make decorators! And I accept arguments: Leonard Sheldon #I am the decorator. Somehow you passed me arguments: Leonard Sheldon #I am the wrapper around the decorated function. #I can access all the variables # - from the decorator: Leonard Sheldon # - from the function call: Rajesh Howard #Then I can pass them to the decorated function #I am the decorated function and only knows about my arguments: Rajesh Howard
好了,上面就是带参数的装饰器.参数能够设置成变量:
c1 = "Penny" c2 = "Leslie" @decorator_maker_with_arguments("Leonard", c1) def decorated_function_with_arguments(function_arg1, function_arg2): print ("I am the decorated function and only knows about my arguments:" " {0} {1}".format(function_arg1, function_arg2)) decorated_function_with_arguments(c2, "Howard") #输出: #I make decorators! And I accept arguments: Leonard Penny #I am the decorator. Somehow you passed me arguments: Leonard Penny #I am the wrapper around the decorated function. #I can access all the variables # - from the decorator: Leonard Penny # - from the function call: Leslie Howard #Then I can pass them to the decorated function #I am the decorated function and only knows about my arguments: Leslie Howard
你能够用这个小技巧把任何函数的参数传递给装饰器.若是你愿意还能够用*args,**kwargs
.可是必定要记住了装饰器只能被调用一次.当Python载入脚本后,你不能够动态的设置参数了.当你运行import x
,函数已经被装饰,因此你什么都不能动了.
好吧,做为奖励,我就给你讲讲如何怎么让全部的装饰器接收任何参数.为了接收参数,咱们用另外的函数来建咱们的装饰器.
咱们包装装饰器.
还有什么咱们能够看到吗?
对了,装饰器!
让咱们来为装饰器一个装饰器:
def decorator_with_args(decorator_to_enhance): """ 这个函数将被用来做为装饰器. 它必须去装饰要成为装饰器的函数. 休息一下. 它将容许全部的装饰器能够接收任意数量的参数,因此之后你没必要为每次都要作这个头疼了. saving you the headache to remember how to do that every time. """ # 咱们用传递参数的一样技巧. def decorator_maker(*args, **kwargs): # 咱们动态的创建一个只接收一个函数的装饰器, # 可是他能接收来自maker的参数 def decorator_wrapper(func): # 最后咱们返回原始的装饰器,毕竟它只是'日常'的函数 # 惟一的陷阱:装饰器必须有这个特殊的,不然将不会奏效. return decorator_to_enhance(func, *args, **kwargs) return decorator_wrapper
下面是如何用它们:
# 下面的函数是你建来当装饰器用的,而后把装饰器加到上面:-) # 不要忘了这个 "decorator(func, *args, **kwargs)" @decorator_with_args def decorated_decorator(func, *args, **kwargs): def wrapper(function_arg1, function_arg2): print "Decorated with", args, kwargs return func(function_arg1, function_arg2) return wrapper # 如今你用你本身的装饰装饰器来装饰你的函数(汗~~~) @decorated_decorator(42, 404, 1024) def decorated_function(function_arg1, function_arg2): print "Hello", function_arg1, function_arg2 decorated_function("Universe and", "everything") #输出: #Decorated with (42, 404, 1024) {} #Hello Universe and everything # Whoooot!
估计你看到这和你刚看完爱因斯坦相对论差很少,可是如今若是明白怎么用就好多了吧.
functools
模块在2.5被引进.它包含了一个functools.wraps()
函数,能够复制装饰器函数的名字,模块和文档给它的包装器.
(事实上:functools.wraps()
是一个装饰器!☺)
#为了debug,堆栈跟踪将会返回函数的 __name__ def foo(): print "foo" print foo.__name__ #输出: foo # 若是加上装饰器,将变得有点复杂 def bar(func): def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ #输出: wrapper # "functools" 将有所帮助 import functools def bar(func): # 咱们所说的"wrapper",正在包装 "func", # 好戏开始了 @functools.wraps(func) def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ #输出: foo
如今遇到了大问题:咱们用装饰器干什么?
看起来很黄很暴力,可是若是有实际用途就更好了.好了这里有1000个用途.传统的用法就是用它来为外部的库的函数(你不能修改的)作扩展,或者debug(你不想修改它,由于它是暂时的).
你也能够用DRY的方法去扩展一些函数,像:
def benchmark(func): """ A decorator that prints the time a function takes to execute. """ import time def wrapper(*args, **kwargs): t = time.clock() res = func(*args, **kwargs) print func.__name__, time.clock()-t return res return wrapper def logging(func): """ A decorator that logs the activity of the script. (it actually just prints it, but it could be logging!) """ def wrapper(*args, **kwargs): res = func(*args, **kwargs) print func.__name__, args, kwargs return res return wrapper def counter(func): """ A decorator that counts and prints the number of times a function has been executed """ def wrapper(*args, **kwargs): wrapper.count = wrapper.count + 1 res = func(*args, **kwargs) print "{0} has been used: {1}x".format(func.__name__, wrapper.count) return res wrapper.count = 0 return wrapper @counter @benchmark @logging def reverse_string(string): return str(reversed(string)) print reverse_string("Able was I ere I saw Elba") print reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!") #输出: #reverse_string ('Able was I ere I saw Elba',) {} #wrapper 0.0 #wrapper has been used: 1x #ablE was I ere I saw elbA #reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {} #wrapper 0.0 #wrapper has been used: 2x #!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A
固然,装饰器的好处就是你能够用它们来作任何事而不用重写,DRY:
@counter @benchmark @logging def get_random_futurama_quote(): from urllib import urlopen result = urlopen("http://subfusion.net/cgi-bin/quote.pl?quote=futurama").read() try: value = result.split("<br><b><hr><br>")[1].split("<br><br><hr>")[0] return value.strip() except: return "No, I'm ... doesn't!" print get_random_futurama_quote() print get_random_futurama_quote() #输出: #get_random_futurama_quote () {} #wrapper 0.02 #wrapper has been used: 1x #The laws of science be a harsh mistress. #get_random_futurama_quote () {} #wrapper 0.01 #wrapper has been used: 2x #Curse you, merciful Poseidon!
Python自身提供了几个装饰器,像property
, staticmethod
.
好大的坑!