閉包普遍使用在函數式編程語言中,雖然不是很容易理解,可是又不得不理解。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中的閉包