一.什么是装饰器?
实际上装饰器就是个函数,这个函数可以为其他函数提供附加的功能。
装饰器在给其他函数添加功能时,不会修改原函数的源代码,不会修改原函数的调用方式。
高阶函数+函数嵌套+闭包 = 装饰器
1.1什么是高阶函数?
1.1.1函数接收的参数,包涵一个函数名。
1.1.2 函数的返回值是一个函数名。
其实这两个条件都很好满足,下面就是一个高阶函数的例子。
def test1():
print "hamasaki ayumi"
def test2(func):
return test1
下面这个代码就满足了对高阶函数所有的条件,
1.1.3 满足以上条件的任意一个条件,就可以称为高阶函数。
如果只是给一个函数添加一个功能,并且不修改原函数的代码,这一点需求,我们通过高阶函数就可以实现,下面是给函数添加一个计算执行时间并不修改函数原有代码的功能。
#!/usr/bin/python2.7
# -*- coding:utf-8 -*-
import time
def foo():
time.sleep(1)
print "hamasaki ayumi <<a best>> 3.28 now on sale!"
def test(func):
start_time = time.time()
func()
stop_time = time.time()
run_time = stop_time - start_time
print run_time
test(foo)
输出结果:
hamasaki ayumi <<a best>> 3.28 now on sale!
1.00331807137
通过上面的测试结果,可以看出,没有修改foo函数的原代码,还给函数增加了一个显示执行时间的功能。
虽然使用高阶函数给原函数foo增加了功能,但是修改了原函数的调用方式,违反了开放封闭原则。
刚刚说了高阶函数接收的参数是一个函数名,我们可以利用这个特性,给函数增加功能(在不修改原代码的前提下)。
那么,高阶函数的另外一个特点,返回值是一个函数,如果应用这种特性,就可以做到不改变函数的调用方式了。那我们就来试一下,只通过高阶函数,是否可以完成装饰器的功能。
import time
def runtime(func):
start_time = time.time()
func()
stop_time = time.time()
run_time = stop_time - start_time
print run_time
return func
def foo():
time.sleep(1)
print "hamasaki ayumi <<a best>> 3.28 now on sale!"
foo = runtime(foo)
foo()
输出结果如下:
hamasaki ayumi <<a best>> 3.28 now on sale!
1.0050868988
hamasaki ayumi <<a best>> 3.28 now on sale!
从得到的结果来看,foo函数被多执行了一次,具体原因,来分析下。
首先调用了runtime(foo),解释器解释到func()的时候,foo函数被执行了一次,runtime函数在return的时候,将foo函数体,return给了foo变量,最后我们在调用foo()的时候,导致foo函数又被多执行了一次,这显然不是我们想要的效果。
所以说,“装饰器”所拥有的功能,单单只通过高阶函数,是无法实现的。
接下来就需要用到剩下的两个知识点了,分别是函数嵌套和函数闭包。
2.什么是函数嵌套?
说到函数嵌套,总会有人以为,在一个函数中调用了另外一个函数,就属于函数嵌套了,就像下面这个例子一样。
def f1():
print ‘f1‘
def f2():
print ‘f2‘
f1()
注意!!这并不是函数嵌套!!
真正的函数嵌套是指,在一个函数中又定义了一个函数。
def f1():
print ‘f1‘
def f2():
print ‘f2‘
print locals()
f1()
像这种在一个函数体内又创建了一个函数,这种形式才属于函数嵌套。
3.什么是闭包函数?
闭包函数,大概可以解释为,在一个内部函数中,对外部作用域(这里的外部作用域指的不是全局作用域!)的变量进行引用,函数内部就可以被理解为是闭包的,下面是一个闭包函数的例子。
a = 1
def f1():
a = 2
def f2():
print a
f2()
f1()
3.1 关于闭包函数的一些使用注意事项。
3.1.1 第一条要注意的就是!闭包函数内部,默认是不可以修改外部作用域的变量的!!(使用nonlocal关键字声明例外)
示例1:
def f1():
x = 1
def f2():
x = 2
print x
print x
f2()
print x
f1()
最后输出的结果是:
1
2
1
在闭包函数中定义的x变量,并没有影响到外部作用域的x变量。
例子2:
def foo():
a = 1
def bar():
a = a + 1
return a
return bar
f = foo()
print f()
分析一下这段代码所存在的问题,在执行foo()函数的时候,python会倒入闭包函数bar,来分析这个作用域的局部变量,其实python在内部规定了,在等号左边的变量都是局部变量,在闭包函数bar中,a赋值在等号的左边,被python认定为这个a是闭包函数bar中的局部变量,在执行print f()时,程序运行到a=a+1的时候,python会在闭包函数bar中找在等号右边的a的值,如果找不到,就报错了。(因为python之前已经把a当做bar闭包函数中的局部变量了。)
那么接下来,我们融合高阶函数+闭包+函数嵌套这三个知识点,来试试看是否可以实现装饰器的功能。
#!/usr/bin/python2.7
# -*- coding:utf-8 -*-
import time
def runtime(func):
def func_in():
start_time = time.time()
func()
stop_time = time.time()
run_time = stop_time - start_time
print run_time
return func_in
def foo():
time.sleep(1)
print "hamasaki ayumi <<a best>> 3.28 now on sale!"
foo = runtime(foo)
foo()
输出的结果:
hamasaki ayumi <<a best>> 3.28 now on sale!
1.00491380692
从结果中可以看出,这次通过上面说的三个知识点,成功初步实现了“装饰器”的功能。
虽然这个通过函数闭包,函数嵌套,高阶函数这三个函数的特性实现了类似装饰器的功能,但是,还是有个小瑕疵,我们看下上个例子的最后两行代码。
foo = runtime(foo)
foo()
如果有很多个函数都要使用前面这个“装饰器”的话,每个函数在调用之前都要重新赋值特别麻烦!
为了更完美的实现装饰器的功能,还需要引入python中的“语法糖”,也就是一个@符号。
这个“@”符号就是python装饰器的语法糖,用法就是@后面加上装饰器的名字,需要使用这个装饰器,“装饰”哪个函数,就把这个装饰器加在函数的上面就可以了。下面是装饰器语法糖的使用示例。
def runtime(func):
def func_in():
start_time = time.time()
func()
stop_time = time.time()
run_time = stop_time - start_time
print run_time
return func_in
@runtime
def foo():
time.sleep(1)
print "hamasaki ayumi <<a best>> 3.28 now on sale!"
foo()
#使用rumtime装饰器,对foo函数做“装饰”。
@runtime = foo = runtime(foo)
这两个语法意义是相同的,只不过不用在每次调用函数之前,都要对函数重新赋值。
本篇文章只是对装饰器的初步了解!关于装饰器的更多内容都在后面的文章中~未完待续~
本文出自 “reBiRTH” 博客,请务必保留此出处http://suhaozhi.blog.51cto.com/7272298/1909347
5.初识python装饰器 高阶函数+闭包+函数嵌套=装饰器
原文地址:http://suhaozhi.blog.51cto.com/7272298/1909347