闭包普遍使用在函数式编程语言中,虽然不是很容易理解,可是又不得不理解。html
#闭包是什么?python
在一些语言中,在函数中能够(嵌套)定义另外一个函数时,若是内部的函数引用了外部的函数的变量,则可能产生闭包。闭包能够用来在一个函数与一组“私有”变量之间建立关联关系。在给定函数被屡次调用的过程当中,这些私有变量可以保持其持久性。 —— 维基百科)sql
举个例子数据库
def sum(a,b): return a+b def sum1(a): def add(b): return a+b #a为外部变量 return add #返回函数 type(sum(1,2)) #<class 'int'> type(sum1(1)) #<class 'function'>
通常支持将函数当作对象使用的编程语言,如Python,JavaScript都支持闭包编程
#如何理解闭包 闭包存在的意义是夹带了外部变量,若是没有的话,其实和普通函数没有区别。同一个函数夹带了不一样的私货就是不一样的闭包,能够理解为对函数的轻量级的封装。闭包
下面这个例子是计算数字的平方和立方,若是是用普通函数,要么是须要写两个函数,要么须要传两个参数app
def rlt(v): def product(num): return num ** v return product square = rlt(2) cube = rlt(3) print(square(2), cube(2)) # 4, 8
闭包传递了某些变量,对使用者来讲就便捷了不少,下面会讲到闭包的原理cors
总结下: 闭包其实和普通函数的区别: 一、普通函数传递变量,闭包传递函数 二、闭包的封装性更好,调用的参数更少编程语言
#何时用闭包?函数式编程
闭包在python中很是常见,可是可能很难意识到闭包的存在。这里不得不提到Python中的装饰器Decorator。 装饰器顾名思义是装饰做用。在代码运行期间动态增长功能的方式,称之为“装饰器”(Decorator)。
举个例子, 计算函数运行时间,正常写法
import time def do_sth(): time.sleep(3) startTime = time.time() do_sth() endTime = time.time() print("do_sth run {} ".format(endTime-startTime)) #do_sth run 3.0005998611450195
若是咱们要计算别的函数运行时间,就要重复屡次代码,咱们把这些重复代码放到装饰器里去,以下面代码
import time def timer(fun): def wrapper(): startTime = time.time() fun() endTime = time.time() print("{} run {}".format(func.__name__, endTime - startTime)) return wrapper @timer def do_sth(): time.sleep(3) timer(do_sth)() # 一:不加@timer语法糖的调用方式 do_sth() #二:加@timer语法糖的调用方式, 和方式一等价
@timer
放到do_sth
的函数定义前,至关于执行了 do_sth = timer(do_sth)
装饰器Pythonic的调用方式彻底和普通函数调用方式同样,是否是很方便?
若是装饰器须要带参数呢?那就须要再加一层,用于接收这些函数。又是一层的闭包。
import time def timer(text): def decorator(fun): def wrapper(): startTime = time.time() fun() endTime = time.time() print("{} {} total run time:{}".format(text, fun.__name__, endTime - startTime)) return wrapper return decorator @timer('excute') def do_sth(): time.sleep(3)
三层嵌套的效果是 do_sth = timer('excute')(do_sth)
想想下面的代码打印结果是什么?
print(do_sth.__name__) print(timer('excute').__name__) print(timer('excute')(do_sth).__name__)
do_sth.__name__
的结果再也不是do_sth
。这里须要把原始函数的__name__
等属性赋值到wrapper()
函数中,不然,有些依赖函数签名的代码执行就会出错。
wrapper.__name__ = func.__name__
? 不用,Python内置的functools.wraps
就是作这个的 另外,以前写的wrapper是不带参数的,只适配不带参数的函数调用,若是是doActive(active, f)
则没法使用。因此更新定义:def wrapper(*args, **kw):
因而,一个完整的不带参数的decorator的写法以下:
import time import functools def timer(fun): @functools.wraps(fun) def wrapper(*args, **kw): startTime = time.time() fun(*args, **kw) endTime = time.time() print("{} {} total run time:{}".format(fun.__name__, endTime - startTime)) return wrapper @timer def do_sth(): time.sleep(3) print(do_sth.__name__) #do_sth
试试改写上面的带参数的decorator
import time import functools def timer(text): @functools.wraps(timer) def decorator(fun): @functools.wraps(fun) def wrapper(): startTime = time.time() fun() endTime = time.time() print("{} {} total run time:{}".format(text, fun.__name__, endTime - startTime)) return wrapper return decorator @timer('excute') def do_sth(): time.sleep(3)
此次以下代码的运行结果是?
print(do_sth.__name__) print(timer('excute').__name__) print(timer('excute')(do_sth).__name__)
经常使用于数据库访问的时候
# 伪代码示意 class QuerySet(object): def __init__(self, sql): self.sql = sql self.db = Mysql.connect().corsor() # 伪代码 def __call__(self): return db.execute(self.sql) def query(sql): return QuerySet(sql) result = query("select name from user_app") if time > now: print result # 这时才执行数据库访问
上面这个不太恰当的例子展现了经过闭包完成惰性求值的功能,可是上面query返回的结果并非函数,而是具备函数功能的类。有兴趣的能够去看看Django的queryset的实现,原理相似。
Python中已经有了很好的解决访问 functools.parial,可是用闭包也能实现。
def partial(**outer_kwargs): def wrapper(func): def inner(*args, **kwargs): for k, v in outer_kwargs.items(): kwargs[k] = v return func(*args, **kwargs) return inner return wrapper @partial(age=15) def say(name=None, age=None): print name, age say(name="the5fire") # 固然用functools比这个简单多了 # 只须要: functools.partial(say, age=15)(name='the5fire')
python偏函数int2 = functools.partial(int, base=2)
,能够类比C++的bind1st, bind2nd
#闭包的原理? 闭包其实也是一种函数,普通函数的__closure__
是None,闭包里是是一个元组,存放着全部的
cell对象,每一个
cell`对象保存着这个闭包里全部的外部变量。
def sum(a, b): return a+b print(sum.__closure__) #None def rlt(v): def product(num): return num ** v return product square = rlt(2) cube = rlt(3) print(square.__closure__) #(<cell at 0x0000000001E2F768: int object at 0x000007FEF25E62B0>,) for x in square.__closure__: print(x.cell_contents) #2 print(cube.__closure__) for x in cube.__closure__: #(<cell at 0x0000000001E2F798: int object at 0x000007FEF25E62D0>,) print(x.cell_contents) #3
参考资料: 廖雪峰 Python装饰器 Python中的闭包