之前用python简单写了一下斐波那契数列的递归实现(如下),发现运行速度很慢。

def fib_direct(n):
assert n > 0, 'invalid n'
if n < 3:
return n
else:
return fib_direct(n - 1) + fib_direct(n - 2)

然后大致分析了一下fib_direct(5)的递归调用过程,如下图:

住:这里的f(2)调用f(1)仅代表基本操作的次数。

可以看到多次重复调用,因此效率十分低。进一步,可以算出递归算法的时间复杂度。T(n) = T(n-1) + T(n-2),用常系数线性齐次递推方程的解法,解出递推方程的特征根,特征根里最大的n次方就是它的时间复杂度O(1.618^n),指数级增长。

为了避免重复调用,可以适当地做缓存,python的装饰器可以完美的完成这一任务。

装饰器:基础


python中一切都是对象,这里需要强调函数是对象

为了更好地理解函数也是对象,下面结合代码片段来说明这一点。

#! /usr/bin/env 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.
Notece 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 meams 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中函数还有一个有趣的特性:函数可以被定义在另一个函数中。下面来看一个简单的例子。

#! /usr/bin/env python
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 define '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

函数引用


前面已经知道函数是对象。那么:

  1. 可以被赋给另一个变量
  2. 可以被定义在另一个函数里

这也意味着,一个函数可以返回另一个函数,下面看一个简单的例子。

#! /usr/bin/env python

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 feek wild:
print get_talk('whisper')()
# outputs: yes...

我们来进一步挖掘一下函数的特性,既然可以返回函数,那么我们也可以把函数作为参数传递

#! /usr/bin/env python

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...
'''

现在,了解装饰器所需要的所有要点我们已经掌握了,通过上面的这一个例子,还可以看出,装饰器其实就是封装器,可以让我们在不修改原函数(whisper)的基础上,在执行原函数的前后执行别的代码。


总结:因为函数是对象,所以有下面四个重点:

  1. 可以被赋给另一个变量
  2. 可以被定义在另一个函数里
  3. 一个函数可以返回另一个函数
  4. 一个函数可以把另一个函数作为参数

手工装饰器


下面我们手工实现一个简单的装饰器。

#! /usr/bin/env python

def my_tiny_new_decorator(a_function_to_decorate):

	'''
Indise, the decorator defines a function on the fly: the wrapper.
This function is going to be wrappered arounded 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 execute 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 readu to use!
'''
return the_wrapper_around_the_original_function # Now imagine you create a function which you don't want to ever touch or modify again.
def a_stand_alone_function():
print 'I am a stand alone function, do not you dare modify me' # Call the function a_stand_alone_function directly
a_stand_alone_function()
# outputs: I am a stand alone function, do not 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_tiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated() '''outputs:
Before the function runs
I am a stand alone function, do not you dare modify me
After the function runs
'''

现在,如果我们想每次调用a_stand_alone_function的时候,实际上调用的是封装后的函数a_stand_alone_function_decorated,那么只需要用a_stand_alone_function去覆盖my_tiny_new_decorator返回的函数即可。也就是:

a_stand_alone_function = my_tiny_new_decorator(a_stand_alone_function)

装饰器阐述


对于前面的例子,如果用装饰器语法,可以添加如下:

@my_tiny_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_tiny_new_decorator 是 another_stand_alone_function = my_tiny_new_decorator(another_stand_alone_function) 的简写。

装饰器只是装饰器设计模式的python实现,python还存在其他几个经典的设计模式,以方便开发,例如迭代器iterators。

当然了,我们也可以嵌套装饰器:

#! /usr/bin/env 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_2(food="--ham_2--"):
print food sandwich_2()

放置装饰器的位置很关键,因为这决定了先执行哪一个装饰器函数

@ingredients
@bread
def strange_sandwich(food="--ham--"):
print food strange_sandwich()
"""outputs:
#tomatoes#
</''''''\>
--ham--
<\______/>
~salad~
"""

装饰器高级用法


给装饰器函数传递参数

当我们调用装饰器返回的函数时,其实是在调用封装函数,给封装函数传递参数也就同样的给被装饰函数传递了参数。

#! /usr/bin/env python
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, so passing arguments to the wra 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
'''

或者不用装饰器的方式做封装器:

'''
Since when you are calling the function returned by the decorator,
you are calling the wrapper, so passing arguments to the wra will
let it pass them to the decorated function
''' def print_full_name(first_name, last_name):
print 'My name is ', first_name, last_name print_full_name = a_decorator_passing_arguments(print_full_name) print_full_name('Peter', 'Venkman')
'''outputs:
I got args! Look: Peter Venkman
My name is Peter Venkman
'''

装饰方法

python中函数和方法几乎一样,除了方法中第一个参数是指向当前对象的引用(self)。这意味着我们可以为方法创建装饰器,只是要记得考虑self。

#! /usr/bin/env python
def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie):
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 do you think?' % (self.age + lie) person = Lucy()
person.sayYourAge(-3)
#outputs: I am 29, what did you think?

我们还可以创建一个通用的装饰器,可以用于所有的方法或者函数,而且不用考虑它的参数的情况。这时候,我们要用到*args,**kwargs。也就是通过包裹位置(元组)或者包裹关键字(字典)传递函数的参数。  

#! /usr/bin/env python
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
function_to_decorate(*args, **kwargs) return a_wrapper_accepting_arbitrary_arguments

另外还有一些高级用法,这里不做详细说明,可以在How can I make a chain of function decorators in Python?进一步深入了解装饰器。

functools.wraps


装饰器封装了函数,这使得调试函数变得困难。不过在python 2.5引入了functools模块,它包含了functools.wraps()函数,这个函数可以将被封装函数(function_to_decorate)的名称、模块、文档拷贝给封装函数(wrapper)。有趣的是,functools.wraps是一个装饰器(my_tiny_new_decorator)。为了更好地理解,看以下代码:  

# 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

但是如果用functool.wraps就会有所改变:

#! /usr/bin/env python
import functools def bar(func):
@functools.wraps(func)
def wrapper():
print 'bar'
return func() return wrapper @bar
def foo():
print 'foo' print foo.__name__
# outputs: foo

具体的做法就是在wrapper函数上,再加上装饰器functools.wraps(func)。

为什么装饰器那么有用


让我们回到本篇文章开始的问题上,重复调用导致递归的效率低下,因此考虑使用缓存机制,空间换时间。这里,就可以使用装饰器做缓存,看下面代码:

http://python.usyiyi.cn/python_278/library/timeit.html

未完成任务:

修改chmod命令的笔记:http://www.cnblogs.com/younes/archive/2009/11/20/1607174.html

完成本博客:http://selfboot.cn/2014/08/10/python_decorator/

装饰器 设计模式:

http://blog.csdn.net/zuoxiaolong8810/article/details/9123533

https://zh.wikipedia.org/wiki/%E4%BF%AE%E9%A5%B0%E6%A8%A1%E5%BC%8F

http://www.runoob.com/design-pattern/decorator-pattern.html

http://www.jellythink.com/archives/171

python函数与方法装饰器的更多相关文章

  1. Python函数篇:装饰器

    装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理. ...

  2. python函数:叠加装饰器、迭代器、自定义迭代器、生成式

    一.叠加多个装饰器二.迭代器三.自定义迭代器四.xxx生成式 一.叠加多个装饰器 # 加载装饰器就是将原函数名偷梁换柱成了装饰器最内层那个wrapper函数 # 在加载完毕后,调用原函数其实就是在调用 ...

  3. python 函数基础及装饰器

    没有参数的函数及return操作 def test1(): print ("welcome") def test2(): print ("welcomt test2&qu ...

  4. python函数学习之装饰器

    装饰器 装饰器的本质是一个python函数,它的作用是在不对原函数做任何修改的同时,给函数添加一定的功能.装饰器的返回值也是一个函数对象. 分类: 1.不带参数的装饰器函数: def wrapper( ...

  5. python学习笔记(五):装饰器、生成器、内置函数、json

    一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...

  6. python笔记5:装饰器、内置函数、json

    装饰器 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象. 先看简单例子: def run(): time.sleep(1 ...

  7. Python学习日记(九) 装饰器函数

    1.import time a.time.time() 获取到当前的时间,返回值为浮点型 import time print(time.time()) #1565422783.6497557 b.ti ...

  8. 第7.26节 Python中的@property装饰器定义属性访问方法getter、setter、deleter 详解

    第7.26节 Python中的@property装饰器定义属性访问方法getter.setter.deleter 详解 一.    引言 Python中的装饰器在前面接触过,老猿还没有深入展开介绍装饰 ...

  9. python基础之函数当中的装饰器

    在实际工作当中存在一个开放封闭原则 1.对扩展是开放的 为什么要对扩展开放呢? 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改.所以我们必须允许代码扩展.添加新 ...

随机推荐

  1. WEB标准:标准定义、好处、名词解释、常用术语、命名习惯、浏览器兼容、代码书写规范

    1. WEB标准是什么? “WEB标准”是一系列标准的总称.一般的误区经常把WEB标准说成DIV+CSS.准确的说法应该是:采用W3C推荐的WEB标准中的XHTML1.1结合CSS2.0 样式表制作页 ...

  2. arm ldr 指令

    ldr 指令格式:(读取概念) ldr{条件} 1目的寄存器,2存储器地址 eg: ldr r0,[r1]; 把r1中数据值读取到r0中: ldr r0,[r1,r2];把r1+r2的数值 读取到r0 ...

  3. Android进程

    android进程等级 1.前台进程 2.可视进程 3.服务进程 4.后台进程 5.空进程 让应用退出方式 finish();让应用成为空进程 Process.killProcess();杀进程 sy ...

  4. eclipse中的debug的用法

    最基本的操作是: 1.首先在一个java文件中设断点,然后debug as-->open debug Dialog,然后在对话框中选类后--> Run 当程序走到断点处就会转到debug视 ...

  5. 模拟Spring依赖注入

    通过读取xml文件,利用反射动态加载类和方法,其实就是spring的注入机制模拟,可以清晰的看出整个运行流程 1.配置文件 applicationContext.xml <beans> & ...

  6. Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class android.support.design.widget.TabLayout,TableLayout引起页面崩溃

    在使用TableLayout的时候,运行引用程序直接Crash. FATAL EXCEPTION: main Process: com.edaixi.activity, PID: 9703 java. ...

  7. iphone iOS7恢复到iOS6教程

    步骤一:首先根据您iOS设备型号,下载最新的iOS 6固件,您可以进入苹果官网下载,也可以以下网址下载. http://sj.zol.com.cn/ios613/

  8. 命名空间 - PHP手册笔记

    概述 命名空间是一种封装事物的方法.在很多地方都可以见到这种抽象概念,比如在操作系统中,目录用来将相关文件分组,对于目录中的文件来说,目录就扮演了命名空间的角色.这个原理应用到程序设计领域就是命名空间 ...

  9. 自动生成XML空节点格式的差异

    我们用C#开发了上位机配置软件,用C开发了嵌入式软件,然后他们之间的参数交互靠XML文件来沟通. C#中添加一个空的节点有以下几种情况. 不给节点的InnerText赋值: <root> ...

  10. SQL Server 找出值得优化的语句

    方法 1. sys.dm_exec_qurey_stats 返回 SQL Server 中缓存查询计划的聚合性能统计信息. 缓存计划中的每个查询语句在该视图中对应一行, 并且行的生存期与计划本身相关联 ...