以前在IMOOC上学习的笔记。今晚整理下发出来,主要是思考怎么表达得清楚又简单,语文不行就是这样,估计这篇文章也说得不太清楚:)
要理解装饰器,先了解函数作用域和闭包。
-
函数作用域的查找顺序概括为:L -> E -> G -> B
local 函数内部作用域
enclosing 函数内部与内嵌函数之间
global 全局作用域
build-in 内置作用域
-
闭包(Closure)就是内部函数中对enclosing作用域的变量进行引用。举例:
# -*-coding:utf-8 -*- def foo(func): def wrapper(x, y): if isinstance(x, int) and isinstance(y, int): return func(x, y) else: print 'Error' return None return wrapper def mysum(x, y): return x + y def mymul(x, y): return x * y mysum_ex = foo(mysum) print mysum_ex(1, 2) print mysum_ex.__closure__ mymul_ex = foo(mymul) print mymul_ex(1, 2) print mymul_ex.__closure__
执行完foo
函数,内部变量被回收,这里是func
变量。但是在执行mysum_ex
函数时,为什么访问得到func
变量呢?因为闭包函数mysum_ex
(实际上就是wrapper
函数)有一个属性__closure__
,存储着需要用到的enclosing 变量,即func
变量。
闭包作用:封装和代码复用。怎么体现?
上面的mysum
用来执行两个整数的加法,mymul
用来执行两个整数的乘法。如果直接执行mysum('1', 2)
或mymul('1', 2)
是会报错:TypeError: cannot concatenate 'str' and 'int' objects
。所以需要添加判断参数是否是int类型的逻辑代码,如mysum
函数
def mysum(x, y):
if isinstance(x, int) and isinstance(y, int):
return x + y
else:
print 'Error'
return None
mymul
函数也需要相同的判断逻辑,所以利用闭包把这部分判断逻辑代码封装到wrapper()
中,通过传递函数对象func
给wrapper()
函数内部使用,来决定到底使用加法操作还是乘法操作。这部分判断参数是否是int类型的逻辑代码达到了代码复用。
最后说装饰器。上面的闭包例子用装饰器来写:
# -*-coding:utf-8 -*-
import functools
def foo(func):
@functools.wraps(func)
def wrapper(x, y):
if isinstance(x, int) and isinstance(y, int):
return func(x, y)
else:
print 'Error'
return None
return wrapper
@foo
def mysum(x, y):
return x + y
print mysum(1, 2)
print mysum('1', 2)
执行结果:
3
Error
None
对于装饰器的几点说明:
装饰器是对闭包的使用。
-
传递一个函数对象
func
给装饰器foo
,foo
返回一个新的函数对象。对于闭包,就不一定要传递函数对象,可以传递其它类型的对象。如:# -*-coding:utf-8 -*- def set_passline(passline): def cmp(val): if val >= passline: print "pass" else: print "failed" return cmp f_100 = set_passline(60) print f_100.__closure__ f_100(89)
传递的是整数对象。
在
def mysum(x, y):
上边加@foo
,是Python提供的语法。表示mysum
这个函数对象作为func
参数传递给foo(func)
函数,再把foo
函数返回的新函数对象赋给mysum
。建议给装饰器里的
wrapper
加上@functools.wraps(func)
装饰器。不加的时候mysum.__name__
的值是wrapper
,加上去值才是mysum
。