函數裝飾器用於在源碼中「標記」函數,以某種方式加強函數的行爲。python
裝飾器是可調用對象,參數是另外一個函數(被裝飾的函數)。緩存
裝飾器可能會處理被裝飾的函數,而後把它返回,或者將其替換成另外一個函數或可調用對象。閉包
裝飾器的特性:(1)把被裝飾的函數替換成其餘函數。(2)裝飾器在加載模塊時當即執行app
變量做用域規則:Python不要求聲明變量,可是假定在函數定義體中賦值的變量是局部變量。函數
閉包:延伸了做用域的函數。包含函數定義體中引用,同時還能夠訪問定義體以外定義的非全局變量。spa
def make_average(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return average
>>> avg = make_average()
>>> avg(10)
在averager函數中,series是自由變量(free variable)指未在本地做用域中綁定的變量。code
Python在 __code__ 屬性中保存 局部變量和自由變量的名稱。對象
>>> avg.__code__.co_varname ('new_value','total') >>> avg.__code__.co_freevars ('series',)
series的綁定在返回的avg對象的__closure__屬性中。blog
avg.__closure__中的各個元素對應於avg.__code__.co_freevars中的一個名稱。這些元素是cell對象。遞歸
它們的值保存在cell_contents屬性中。
>>> avg.__code__.co_freevars ('series',) >>> avg.__closure__ (<cell at 0x000001C2EBE261C8: list object at 0x000001C2EBE33988>,) >>> avg.__closure__[0].cell_contents [10,]
nonlocal聲明:把變量標記爲自由變量。
def make_averager(): count = 0 total = 0 def averager(new_value): nonlocal count,total count += 1 total += new_value return total/count return average
當變量是數字或任何不可變類型時,averager的定義體中爲count賦值,實質會隱式建立局部變量count。
引入nonlocal聲明,把變量標記爲自由變量,即便在函數中爲變量賦予新值,也會變成自由變量。
functools.lru_cache 實現了備忘功能。把耗時的函數結果保存起來,避免傳入相同的參數時重複計算。
遞歸函數適合使用lru_cache。
functools.lru_cache(maxsize=128,typed=Fales)
maxsize參數指定存儲多少個調用的結果。緩存滿了後,舊得結果會被扔掉。maxsize應該設置爲2的冪
typed參數爲True,不一樣參數類型獲得的結果分開保存。
▲ lru_cache使用字典存儲結果,鍵根據調用時傳入的定位參數和關鍵字參數建立,因此被裝飾的函數全部參數必須是可散列的。
functools.singledispatch 裝飾器能夠把總體方案拆分紅多個模塊。
根據第一個參數的類型,以不一樣的方式執行相同操做的一組函數。
from functools import singledispatch from collections import abc import numbers @singledispatch def inp_check(obj): print('Main inp_check') @inp_check.register(str) def _(text): print('Text %s'%text) @inp_check.register(numbers.Integral) def _(n): print('Int %s' %n) @inp_check.register(tuple) @inp_check.register(abc.MutableSequence) def _(seq): print('List %s'%seq)
註冊的專門函數應該處理抽象基類(numbers.Integral和abc.MutableSequence),不需處理具體實現(int、list)。
這樣,代碼支持的兼容類型更普遍。
Python把被裝飾的函數做爲第一個參數傳遞給裝飾器函數,那麼如何給裝飾器傳遞其餘參數呢?
def outer(func): def inner(*args,**kwargs): print('Do Something..') result = func(*args,**kwargs) return result return inner @outer def f1(num): print('Number %s'%num) # ========= 等效於
f1 = outer(f1)
咱們能夠建立一個裝飾器工廠函數,把參數傳給它,返回一個裝飾器,而後再把它運用到要裝飾的函數上。
def register(active=False): def outer(func): def inner(*args,**kwargs): print('Do Something..') result = func(*args,**kwargs) return result return inner return outer @register(active=True) def f1(num): print('Number %s'%num) # =========等效於 f1 = register(active=True)(f1)