本文目錄: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