闭包
之前一直不明白闭包的定义-内层函数引用了外层函数的变量(包括它的参数)就构成了闭包。我觉得也没什么了不起的,干嘛还专门起一个别扭的名字闭包呢。后来发现,其实并不是想的这么简单,内层函数调用外层变量的时候,这时候外层函数已经调用结束返回了,理论上外层函数所有的局部变量都已经释放,所以内层函数不应该拿到外层函数的变量(包括它的参数)。闭包就是将使用到的变量(称为环境变量)绑定到内层函数的__closure__
属性上。
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager
avg = make_averager()
print(avg.__code__.co_freevars[0]) # series
print(avg.__closure__[0].cell_contents) # []
avg(10)
avg(12)
print(avg.__code__.co_freevars[0]) # series
print(avg.__closure__[0].cell_contents) # [10, 12]
装饰器
装饰器就是在不改变原函数的调用方式的情况下,在函数的前后添加功能。装饰器完美的展示了开放封闭原则,即对扩展开放,对修改封闭。
关于装饰器的一些概念
- 普通装饰器:@decorator
- 装饰器工厂函数:本身不是装饰器,返回的是装饰器 @decoratorfunc(args) <=>@decorator
- 参数化装饰器:通过装饰器工厂函数的方式,将参数转给装饰器,典型的就是Flask的路由
- 类装饰器
- 叠放装饰器
普通装饰器
装饰器函数decorator定义的时候参数必须是一个函数。
普通装饰器使用@decorator
语法糖装饰函数时是不带参数的,它会默认将被装饰的函数作为它的参数进行传递
def decorator(func):
def inner(*args,**kwargs):
"在被装饰函数之前要做的事"
ret = func(*args,**kwargs)
"在被装饰函数之后要做的事"
return ret
return inner
@decorator
def myfunc(*args,**kwargs):
print('im myfunc')
解释器会将@decorator装饰过程解释成下面这样的语句:
myfunc = decorator(myfunc) # 此时myfunc->inner
也就是说就算我没有调用myfunc函数,就已经首先调用了decorator函数了。
带参数的装饰器
当我们看到@decoratorfunc(arg1,arg2)
这种带有参数的装饰器的时候,我们首先意识到decoratorfunc
一定不是装饰器函数,而是装饰器工厂函数,它返回的是装饰器。这样子通过装饰器工厂函数将参数传递给内部进行处理。通常带参数的装饰器的定义会是三层函数嵌套,第一层返回装饰器,第二层返回被装饰函数,第三层根据需求返回或者不返回。
叠放装饰器
@decorator_one
@decorator_two
def func():
pass
相当于:
func = decorator_one(decorator_two(func))
类装饰器
类装饰器一般都实现了__call__
方法,请看下例
# 定义一个Time类
class Time(object):
def __init__(self,func): # 注意这里要设定参数接收Test
self._func = func
def __call__(self):
print('我可以对函数进行装饰')
self._func()
@Time # 等价于 Test = Time(Test),此时Test是一个实例对象
def Test():
print('测试一下')
Test() # 调用__call__方法
#结果为:
#我可以对函数进行装饰
#测试一下
装饰器引发的问题
使用装饰器会覆盖被装饰函数的一些属性,例如 __name__
,__doc__
,因此可以使用@warps(func)装饰器来恢复被装饰函数myfunc的相关属性
from functiontools import wraps
def decorator(func):
@wraps(func)
def inner(*args,**kwargs):
"在被装饰函数之前要做的事"
ret = func(*args,**kwargs)
"在被装饰函数之后要做的事"
return ret
return inner
@decorator
def myfunc(*args,**kwargs):
print('im myfunc')
print(myfunc__name__) # myfunc
装饰器的案例
1.注册回调函数
下面这个示例展示了通过URL的路由来调用相关注册的函数示例:
class MyApp():
def __init__(self):
self.func_map = {}
def register(self, name):
def func_wrapper(func):
self.func_map[name] = func
return func
return func_wrapper
def call_method(self, name=None):
func = self.func_map.get(name, None)
if func is None:
raise Exception("No function registered against - " + str(name))
return func()
app = MyApp()
@app.register('/')
def main_page_func():
return "This is the main page."
@app.register('/next_page')
def next_page_func():
return "This is the next page."
print app.call_method('/')
print app.call_method('/next_page')
注意:
1)上面这个示例中,用类的实例来做decorator。
2)decorator类中没有__call__()
,但是wrapper返回了原函数。所以,原函数没有发生任何变化。
2.线程异步
下面量个非常简单的异步执行的decorator,注意,异步处理并不简单,下面只是一个示例。
from threading import Thread
from functools import wraps
def async(func):
@wraps(func)
def async_func(*args, **kwargs):
func_hl = Thread(target = func, args = args, kwargs = kwargs)
func_hl.start()
return func_hl
return async_func
if __name__ == '__main__':
from time import sleep
@async
def print_somedata():
print 'starting print_somedata'
sleep(2)
print 'print_somedata: 2 sec passed'
sleep(2)
print 'print_somedata: 2 sec passed'
sleep(2)
print 'finished print_somedata'
def main():
print_somedata()
print 'back in main'
print_somedata()
print 'back in main'
main()
3.给函数调用做缓存
from functools import wraps
def memo(fn):
cache = {}
miss = object()
@wraps(fn)
def wrapper(*args):
result = cache.get(args, miss)
if result is miss:
result = fn(*args)
cache[args] = result
return result
return wrapper
@memo
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
上面这个例子中,是一个斐波拉契数例的递归算法。我们知道,这个递归是相当没有效率的,因为会重复调用。比如:我们要计算fib(5),于是其分解成fib(4) + fib(3),而fib(4)分解成fib(3)+fib(2),fib(3)又分解成fib(2)+fib(1)…… 你可看到,基本上来说,fib(3), fib(2), fib(1)在整个递归过程中被调用了两次。
而我们用decorator,在调用函数前查询一下缓存,如果没有才调用了,有了就从缓存中返回值。一下子,这个递归从二叉树式的递归成了线性的递归。
网友评论