函數裝飾器和閉包

 

裝飾器的做用

  函數裝飾器用於在源碼中「標記」函數,以某種方式加強函數的行爲。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)
相關文章
相關標籤/搜索