原文出自stackoverflow,英文好的同学可以直接戳这里,如果是人人看的那么可能排版不太好,可以戳这里
Python的函数也是对象
为了理解装饰器,你必须首先要理解python里的函数也是对象。这是一个很重要的概念,我们可以来看个小例子:
def shout(word="yes"): return word.capitalize()+"!" print shout() # 输出 : 'Yes!' # 作为一个对象,你可以像其他对象一样,将他赋给一个变量 scream = shout # 注意我们没有使用shout() 因为我们不是在调用shout这个函数, # 我们把shout这个函数传递给给scream # 这意味着我们可以调用scream来代替调用shoot print scream() # 输出 : 'Yes!' # 不仅如此,你删掉shout, 仍然可以调用scream del shout try: print shout() except NameError, e: print e #outputs: "name 'shout' is not defined" print scream() # 输出: 'Yes!'我们先把上面的概念记下,过会我们会再回来。 ## Python里函数的另外一个特性:函数可以定义在另外一个函数内(函数中定义函数)
def talk(): # 你可以在函数“talk”中定义一个函数 def whisper(word="yes"): return word.lower()+"..." # 然后在“talk”函数里直接使用“whisper” print whisper() # 你调用了内部定义了“whisper”的talk函数, # 每一次调用talk,talk中的whisper也会被调用 talk() # 输出: # "yes..." # 但是在talk函数之外并不存在whisper: try: print whisper() except NameError, e: print e # 输出 : "name 'whisper' is not defined"
##
##
函数引用
从上面的例子可以看出函数是对象,因此:
- 可以传递给变量
可以在另一个函数中定义
这意味着一个函数可以返回另外一个函数(返回值是一个函数),我们看一下例子: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()
输出 : Yes!
你也可以直接用一种很难看的方式直接调用
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): # 装饰器在内部定义了一个函数:the_wrapper_around_the_original_function # 这是一个对原始函数(a_function_to_decorate)进行包装的函数 # 所以它可以在原始函数(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_todecorate还没有被执行(这是定义过程) # 我们返回我们刚刚创建的装饰器函数:the_wrapper_around_the_original_function # 这个装饰器包括了原函数以及我们在其之前和之后需要执行的代码 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_decorated
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_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)装饰器是 设计模式中装饰者模式的python实现。这些经典的设计模式都 被内嵌在python中来简化开发,就像iterators(迭代器)一样。 当然,你可以同时使用多个装饰器(手动装饰器实现):
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语法糖实现:
@bread @ingredients def sandwich(food="--ham--"): print food sandwich() #输出: #</''''''> # #tomatoes# # --ham-- # ~salad~ #<______/> The order you set the decorators MATTERS: @ingredients @bread def strange_sandwich(food="--ham--"): print food strange_sandwich() #输出: ##tomatoes# #</''''''> # --ham-- #<______/> # ~salad~接下来我们来看一些关于装饰器的高级用法 向被装饰的函数传递参数
# 这不是什么黑魔法,你只需像装饰器传递参数 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_wrapper_accepting_arguments这个函数, # 把参数传递给wrapper然后再让它把参数传递给被装饰的原函数 @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之后,你用相同的办法可以为方法创建装饰器,
def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie):
lie = lie - 3 # very friendly, decrease age even more :-)
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?
当然,如果你想创建一个通用与函数(function)与方法(method)的装饰器,不需要考虑参数如何,只要用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): # You can now add a default value
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 a 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 a ordinary function
它输出 “I am a ordinary function”, 因为你调用了它
@my_decorator
def lazy_function():
print “zzzzzzzz”
输出: I am a ordinary function
my_decorator的调用和使用@my_decorator是十分相似的。你让通过被变量标志的my_decorator来让python调用它。这很重要,因为这个标志可以让你直接指出装饰器
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)
outputs:
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 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()
#outputs:
#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):
# 向这传递参数是一种闭包特性
# 如果你不喜欢这种闭包特性,你可以把它传给一个变量
# 或者看看这: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 variablesn"
"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
做个练习:一个用来装饰装饰器的装饰器
def decorator_with_args(decorator_to_enhance):
# 我们像之前那样传递参数
def decorator_maker(*args, **kwargs):
# 创建一个只接受函数作为参数的函数
# 但是能够使用maker传递的参数
def decorator_wrapper(func):
# 我们将返回原始的装饰器,就像返回一个普通的函数一样
# IS JUST AN ORDINARY FUNCTION (which returns a function).
# 需要注意的是装饰器必须有自己的标识符,否则无法工作
return decorator_to_enhance(func, *args, **kwargs)
return decorator_wrapper
return decorator_maker</pre>
然后可以这么使用:
# 你创造了了一个将作为装饰器使用的函数,然后向里面插入了一个装饰器
别忘了,插入的装饰器的标识符是 “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
酷不?
我知道,你上次有这种感觉,应该是听到别人说:“要理解递归,你要先理解递归”的时候。但是掌握了刚才讲的这些之后是不是很爽。
##
装饰器范例
- 装饰器是Python2.4出现的新特性,确保你的代码跑在2.4或者更高版本上
- 记住装饰器会延缓函数调用
- 不能取消装饰。有一些方法能创建可以取消的装饰器,但木有人用。一个函数被装饰了,就是被装饰了。
装饰器装饰函数后,会增加debug难度
Python2.5通过提供functool模块来解决了上述的最后一个问题。functool.wraps会拷贝被装饰函数的名字 模块 文档等到他的包装器中,而functools.wraps
本身也是一个装饰器# 直接用装饰器会显得很杂乱
def bar(func):
def wrapper():
return wrapperprint "bar" return func()
@bar
def foo():
print “foo”
print foo.name
#outputs: 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
如何有效的使用装饰器
也许你会有个疑问:我可以用装饰器做什么,它看起来很好很强大,但是如果有个实际例子会更好。
关于在什么情况下使用有很多种可能,传统的用法是当你需要扩展一个外部库函数的行为或者处于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
而使用装饰器的好处是是不需要做多余的重写就能立刻使用:
@counter
@benchmark
@logging
def get_random_futurama_quote():
import httplib
conn = httplib.HTTPConnection(“slashdot.org:80”)
conn.request(“HEAD”, “/index.html”)
for key, value in conn.getresponse().getheaders():
if key.startswith(“x-b”) or key.startswith(“x-f”):
return value
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等等。Django使用装饰器来捕获和查看权限等,装饰器用途广泛