原文地址html
以前用python简单写了一下斐波那契数列的递归实现(以下),发现运行速度很慢。python
def fib_direct(n): assert n > 0, 'invalid n' if n < 3: return 1 else: return fib_direct(n - 1) + fib_direct(n - 2)
而后大体分析了一下fib_direct(5)的递归调用过程,以下图:git
能够看到屡次重复调用,所以效率十分低。进一步,能够算出递归算法的时间复杂度
。T(n) = T(n-1) + T(n-2),用常系数线性齐次递推方程的解法,解出递推方程的特征根,特征根里最大的n次方就是它的时间复杂度O(1.618^n),指数级增加。github
为了不重复调用,能够适当地作缓存,python的装饰器能够完美的完成这一任务。算法
<!-- more -->编程
python中一切都是对象,这里须要强调函数是对象
。为了更好地理解函数也是对象,下面结合代码片断来讲明这一点。设计模式
def shout(word="yes"): return word.capitalize() + "!" print shout() # outputs: Yes! """ As an object, you can assign the function to a variable like any other object. Notice we don't use parentheses: we are not calling the function, we are putting the function "shout" into the variable "scream". """ scream = shout print scream() # outputs: Yes! """ More than that, it means you can remove the old name 'shout', and the function will still be accessible from 'scream'. """ del shout try: print shout() except NameError, e: print e # outputs: name 'shout' is not defined print scream() # outputs: 'Yes!'
由于函数是对象,因此python中函数还有一个有趣的特性:函数能够被定义在另外一个函数中
。下面来看一个简单的例子。api
def talk(): # You can define a function on the fly in "talk" def whisper(word="yes"): return word.lower()+"..." print whisper() """ You call "talk", that defines "whisper" EVERY TIME you call it, then "whisper" is called in "talk". """ talk() # outputs: yes... # But "whisper" DOES NOT EXIST outside "talk". try: print whisper() except NameError, e: print e # outputs : name 'whisper' is not defined
前面已经知道函数是对象。那么:缓存
能够被赋给另外一个变量app
能够被定义在另外一个函数里
这也意味着,一个函数能够返回另外一个函数
,下面看一个简单的例子。
def get_talk(kind="shout"): def whisper(word="yes"): return word.lower() + "..." def shout(word="yes"): return word.capitalize() + "!" return whisper if kind == "whisper" else shout # Get the function and assign it to a variable talk = get_talk() # You can see that "talk" is here a function object: print talk # outputs : <function shout at 0x107ae9578> print talk() # outputs : Yes! # And you can even use it directly if you feel wild: print get_talk("whisper")() # outputs : yes...
咱们来进一步挖掘一下函数的特性,既然能够返回函数
,那么咱们也能够把函数做为参数传递
。
def whisper(word="yes"): return word.lower() + "..." def do_something_before(func): print "I do something before." print "Now the function you gave me:\n", func() do_something_before(whisper) """outputs I do something before. Now the function you gave me: yes... """
如今,了解装饰器所须要的全部要点咱们已经掌握了,经过上面的例子,咱们还能够看出,装饰器其实就是封装器
,可让咱们在不修改原函数的基础上,在执行原函数的先后执行别的代码。
下面咱们手工实现一个简单的装饰器。
def my_shiny_new_decorator(a_function_to_decorate): """ Inside, the decorator defines a function on the fly: the wrapper. This function is going to be wrapped around the original function so it can execute code before and after it. """ def the_wrapper_around_the_original_function(): """ Put here the code you want to be executed BEFORE the original function is called """ print "Before the function runs" # Call the function here (using parentheses) a_function_to_decorate() """ Put here the code you want to be executed AFTER the original function is called """ print "After the function runs" """ At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED. We return the wrapper function we have just created. The wrapper contains the function and the code to execute before and after. It’s ready to use! """ return the_wrapper_around_the_original_function # Now imagine you create a function you don't want to ever touch again. 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 """ Well, you can decorate it to extend its behavior. Just pass it to the decorator, it will wrap it dynamically in any code you want and return you a new function ready to be used: """ 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 """
如今,若是咱们想每次调用a_stand_alone_function
的时候,实际上调用的是封装后的函数a_stand_alone_function_decorated
,那么只须要用a_stand_alone_function去覆盖my_shiny_new_decorator返回的函数便可。也就是:
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
对于前面的例子,若是用装饰器语法,能够添加以下:
@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 """
对了,这就是装饰器语法,这里的@my_shiny_new_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() # outputs: --ham-- sandwich = bread(ingredients(sandwich)) sandwich() """outputs: </''''''\> #tomatoes# --ham-- ~salad~ <\______/> """
用python的装饰器语法,以下:
@bread @ingredients def sandwich_2(food="--ham_2--"): print food sandwich_2()
放置装饰器的位置很关键。
@ingredients @bread def strange_sandwich(food="--ham--"): print food strange_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 """ Since when you are calling the function returned by the decorator, you are calling the wrapper, passing arguments to the wrapper will let it pass them to the decorated function """ @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 """
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) # outputs: I am 26, what did you think?
咱们还能够建立一个通用的装饰器,能够用于全部的方法或者函数,并且不用考虑它的参数状况。这时候,咱们要用到*args, **kwargs
。
def a_decorator_passing_arbitrary_arguments(function_to_decorate): # The wrapper accepts any arguments def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs): print "Do I have args?:" print args print kwargs # Then you unpack the arguments, here *args, **kwargs # If you are not familiar with unpacking, check: # 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
另外还有一些高级用法,这里不作详细说明,能够在How can I make a chain of function decorators in Python?进一步深刻了解装饰器。
装饰器封装了函数,这使得调试函数变得困难。不过在python 2.5引入了functools
模块,它包含了functools.wraps()
函数,这个函数能够将被封装函数的名称、模块、文档拷贝给封装函数。有趣的是,functools.wraps是一个装饰器。为了更好地理解,看如下代码:
# For debugging, the stacktrace prints you the function __name__ def foo(): print "foo" print foo.__name__ # outputs: foo def bar(func): def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ # outputs: wrapper import functools def bar(func): # We say that "wrapper", is wrapping "func" # and the magic begins @functools.wraps(func) def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ # outputs: foo
让咱们回到本篇文章开始的问题上,重复调用致使递归的效率低下,所以考虑使用缓存机制,空间换时间。这里,就可使用装饰器作缓存,看下面代码:
from functools import wraps def cache(func): caches = {} @wraps(func) def wrap(*args): if args not in caches: caches[args] = func(*args) return caches[args] return wrap @cache def fib_cache(n): assert n > 0, 'invalid n' if n < 3: return 1 else: return fib_cache(n - 1) + fib_cache(n - 2)
这样递归中就不会重复调用,效率也会提升不少。具体能够看这里,从执行时间很容易看出作了缓存以后速度有了很大的提高。装饰器还能够用来扩展外部接口函数(一般你不能修改它)的功能,或者用来调试函数。其实,装饰器能够用于各类各样的场合!
python自己提供了一些装饰器:property,staticmethod,等等。另外,Django使用装饰器去管理缓存和权限。
计算斐波纳契数,分析算法复杂度
How can I make a chain of function decorators in Python?
Python装饰器与面向切面编程
how to use args and kwargs in python?
Fibonacci, recursion and decorators