本文目录:html
1. 闭包的解析和用法python
2. 函数式装饰器编程
3. 类装饰器闭包
1、闭包app
闭包是一种函数,从形式上来讲是函数内部定义(嵌套)函数,实现函数的扩展。在开发过程当中,考虑到兼容性和耦合度问题,若是想在原有的函数基础上添加东西而又不改动原有函数的结构,一般会使用闭包。但闭包的功能还不仅是这个。实际上,闭包会保留定义函数时存在的自由变量的绑定,这样在调用函数时,虽然定义做用域不可用了,可是仍然可使用那些绑定的变量。简单来讲,普通函数在调用完后,函数内部的变量就释放了(由于直接调用的函数没有绑定在某一个对象上,Cpython解析器就会把它回收了),而闭包内部的变量仍然保存着。函数
普通函数不会保存变量的值:spa
例如: def function(value): nums = [] nums.append(value) return nums func = function(2) func2 = function(3) print(func) # [2] print(func2) # [3] 两次调用函数返回的值都是不相同的
在闭包中,外部函数的变量一直会为内部函数“保留”着,每次调用函数均可以获取这些变量code
def closure(): nums =[] def function(value): nums.append(value) return nums return function close = closure() close(5) close(6) close(7) # 元组形式返回嵌套函数function的变量 print(close.__code__.co_varnames) #('value',) print(close.__code__.co_freevars) #('nums',) # 列表形式保存嵌套函数中自由变量的值 print(close.__closure__[0].cell_contents) #[5, 6, 7]
闭包的执行顺序能够理解为:htm
closure(function(value))对象
所以close = closure()至关于为闭包建立了一个绑定的对象,这个对象内部有变量nums和函数function。当变量在下一次调用的时候,这些量还会保存着。所以当屡次调用close()的时候,nums列表会更新。
以上代码的解析:
对象审查(反射)
_ _code_ _返回对象中的函数
_ _co_varnames 返回内部函数中保存的变量,例如function中的value
_ _co_freenames 返回内部函数中的自由变量,自由变量是编程中的一个专业名词。
如上面的代码中,nums并非在function函数中绑定的,它是在它的外部函数的做用域范围内绑定的,因此在function内部,nums是一个自由变量。而close对象为function函数保留了这个自由变量,在每次调用函数时,均可以更新这个自由变量。
自由变量(全部的)实际保存在闭包中,能够经过_ _closure_ _来获取,它是一个列表,每一个元素都表示一个自由变量,如上面的nums。每一个元素都是一个cell,它的属性cell_contents保存着这个自由变量的值,所以有:
_ _closure_ _[0].cell_contents
更多关于函数/类/生成器的审查能够参考官方文档:
https://docs.python.org/2/library/inspect.html
2、函数形式的装饰器
上面讲了如何经过嵌套函数实现一个闭包,下面将装饰器是如何实现的。实际上,装饰器离不开对闭包的理解,函数形式的装饰器看起来像是闭包换了一种表达形式,调用起来更灵活和更方便。
例如:
# 函数形式的闭包 registry = [] def decorator(func): print('registe %s'%func) registry.append(func) return func # 返回的量必须是一个函数,不然会报错 @decorator def fun1(): print('running fun1') @decorator def fun2(): print('running fun2') @decorator def fun3(): print('running fun3') fun1() fun2() fun3()
结果:
registe <function fun1 at 0x000002234D891378> registe <function fun2 at 0x000002234D891400> registe <function fun3 at 0x000002234D891488> running fun1 running fun2 running fun3
装饰器看起来有点像闭包,只不过是加了一个@的外壳,而这个外壳函数的参数必须是一个函数,而且必需要有返回函数(返回的通常是内部函数)
装饰器的执行顺序:
decorator(func)
内部函数的参数能够在函数调用时传入,而没必要像闭包那样必须由对象传入。
值得注意的是:装饰器函数有导入时运行和运行时运行的区别,装饰器在模块导入的时候就执行了,而被装饰的函数则在调用的时候才执行。
上面这个被装饰器“装饰”的函数彷佛看起来跟装饰器“互动”不多,那么下面结合装饰器和闭包实现一个更复杂的装饰器:
def decorator(func): def outerFunc(*args): # 装饰器内部实现闭包,闭包的外部函数接受任意定位变量 outerFunName = outerFunc.__name__ innerFunName = func.__name__ print('change innerFunc:%s to outerFunc:%s'%(innerFunName, outerFunName)) result = func(*args) # 能够实现,由于闭包中保存了自由变量func result += " and start running" return result return outerFunc # 将改变返回的函数,返回外部函数 @decorator def fun(str): return str str = fun('This is funciton1') # change innerFunc:fun to outerFunc:outerFunc print(str) # This is funciton1 and start running
装饰器执行顺序:
decorator(outerFunc(func(args)))
这个装饰器内部的闭包实现仍是比较简单的,只是为了说明原理,在编程过程当中能够根据实际添加更多的功能实现。
继续改造,让装饰器也带上参数:
# 带参数装饰器 def decorator(name): def _decorator(func): def outerFunc(*args): print(name) outerFunName = outerFunc.__name__ innerFunName = func.__name__ print('change innerFunc:%s to outerFunc:%s'%(innerFunName, outerFunName)) result = func(*args) result += " and start running" return result return outerFunc return _decorator @decorator(name='@Author:Tom') def fun(str): return str str = fun('This is funciton1') print(str) 结果: @Author:Tom change innerFunc:fun to outerFunc:outerFunc This is funciton1 and start running
3、类形式的装饰器
讲完了函数形式的装饰器,那么接下来说讲类形式的装饰器
通常类的定义以下:
class Test(object): def __init__(self,name): self._m = 0 self._n = 0 self.name = name def test_print(self): print(self.name)
而若是想要将一个类变成一个装饰器,那么就须要一个很关键的魔法方法_ _call_ _(),它的做用是将一个类实例变成可调用的,改造一下上面的类:
class Test(object): def __init__(self): self.count = 0 def __call__(self): # print(self.count) self.count += 1 # 每一次调用这个类实例都记录一次 return self.count # __call__函数将类实例变成可调用形式,而实际上还会有一个返回量(变量/函数),所以须要写return,不然返回为None test = Test() # 以函数调用的形式直接调用类实例 print(test()) # 1 print(test()) # 2
这样看起来,类形式的装饰器有点像函数形式的装饰器,它也保存了一些变量。实际上这点不足为奇,由于,原本类实例的变量已经绑定在类实例对象中。
还能够这样用:
class Average: def __init__(self): self.values = [] # 每次调用average实例都会更新self.values def __call__(self, newvalue): self.values.append(newvalue) total = sum(self.values) average = total / len(self.values) return average average = Average() print(average(6)) print(average(7)) print(average(8)) 结果: 6.0 6.5 7.0
类装饰器:
class Decorator: def __init__(self, add=1): # 定义能够传入的参数 self.count = 0 self.add = add def __call__(self, fun): self.fun = fun return self._call_func def _call_func(self): self.count += self.add return self.fun(self.count) # 至关于Decorate(count) @Decorator(add=2) # 改变传入的参数值 def count(cnt): print(cnt) count() # 2 count() # 4
笔者认为类形式的装饰器会比函数形式的装饰器更加灵活和方便,由于它的内部实现能够更灵活,看起来也比较符合平常使用的习惯,由于函数式的装饰器看起来总有一点怪怪的(笔者本人见解而已)。实际使用中就要根据业务需求来选择了
参考文章:
1. 《流畅的python》
2. https://docs.python.org/2/library/inspect.html