函数是 object 对象。此外,函数还:
-
可以像变量一样赋值
-
可以在另一个函数内部定义
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 #outputs : <function shout at 0xb7ea817c> # The object is the one returned by the function: # 这个对象是由一个函数返回的 print talk() #outputs : Yes! # 如果你觉得奇怪的话,你甚至可以直接使用它
# 你甚至还可以把函数作为参数进行传递
print getTalk('whisper')() #outputs : 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_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() #outputs: 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() #outputs: #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() #outputs: #Before the function runs #Leave me alone #After the function runs
当然你可以堆叠多个修饰器:
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~ #<\______/>
使用修饰器语法:
@bread @ingredients def sandwich(food='--ham--'): print food sandwich() #outputs: #</''''''> # #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_decorator_passing_arguments def print_full_name(first_name, last_name): print 'My name is', first_name, last_name print_full_name('Peter', 'Venkman') # outputs: #I got args! Look: Peter Venkman #My name is Peter Venkman
装饰器里面的wrapper的参数上面的一些注意事项:
def wrapper(self, lie): #定义类方法加self def wrapper(*args, **kwargs): #创建通用的包裹器
装饰器maker,装饰器,被修饰的func函数:
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() #outputs: #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() #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.
使用修饰器语法缩短代码:
@decorator_maker() def decorated_function(): print 'I am the decorated function.' #outputs: #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. ' 'I can access all the variables ' ' - from the decorator: {0} {1} ' ' - from the function call: {2} {3} ' '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') #outputs: #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
额外的注意事项
# 至于调试,stacktrace 输出函数的 __name__ def foo(): print 'foo' print foo.__name__ #outputs: foo # 有了装饰器之后,有点混乱 def bar(func): def wrapper(): print 'bar' return func() return wrapper @bar def foo1(): print 'foo1'
@bar
def foo2():
print 'foo2'
print foo1.__name__ #outputs: wrapper
print foo2.__name__
#重复 output: wrapper报错
# `functools` 可以解决上面的情况 import functools def bar(func): # 我们认为 `wrapper` 正在包装 `func` # 神奇的事情发生了 @functools.wraps(func) def wrapper(): print 'bar' return func() return wrapper @bar def foo1(): print 'foo1'
def foo2():
print 'foo2'
print foo1.__name__ #outputs: foo1
print foo2.__name__
#outputs: foo2
修饰器的应用场景:
def benchmark(func): """ 一个用来输出函数执行时间的装饰器 """ 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): """ 一个用来记录脚本活动的装饰器。 (实际上只是打印出来,但可以输出到日志!) """ def wrapper(*args, **kwargs): res = func(*args, **kwargs) print func.__name__, args, kwargs return res return wrapper def counter(func): """ 一个用来统计并输出函数执行次数的装饰器 """ 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') #outputs: #reverse_string ('Able was I ere I saw Elba',) {} #wrapper 0.0 #wrapper has been used: 1x #ablE was I ere I saw elbA
总结:修饰器就是不修改之前代码前提下,添加修饰代码的方法。使修饰器带参数就再包裹一层decorator_maker(decorator_args),
里面的decorator(func_args),再里面是func。一般用在统计运行时间,日志等作用。
接下来看看python的自带装饰器:
@property : 使调用的类方法像引用类的字段属性一样。getter()和setter()方法,修改私有变量。
@staticmethod,abstractmethod,classroom
<a href="http://www.jobbole.com/members/bread">@bread</a>
@ingredients
def sandwich(food='--ham--'):
print food
sandwich()
#outputs:
#</''''''>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>