Python提高:關於閉包和裝飾器

賈富程發表於2018-09-28

閉包

閉包是指在一個函式中定義了一個另外一個函式,內函式裡運用了外函式的臨時變數,並且外函式的返回值是內函式的引用,這樣就構成一個閉包。 例如以下:

def callFunc():
    n = 1
    def show():
        print('show: ', n)
    return show

s = callFunc()
s()複製程式碼

去掉了全域性變數的使用。而且將 show 函式封裝在了 callFunc 函式內部,使外部不可見,不能使用 show 函式,隱藏了實現細節

程式在執行時,callFunc 函式返回了內部定義的 show 函式,並且 在 show 函式內部使用了外部函式的變數。

在 show 函式返回時,儲存了當前的執行環境,也就是會在 show 函式中使用的外部變數 n 。

因為 n 是一個 callFunc 函式中的區域性變數,正常情況下 callFunc 函式執行結束後,n 就會被釋放。

但是現在因為 callFunc 函式中返回了 show 函式,show 函式在外部還會再執行,所以程式會將 show 函式所需的執行環境儲存下來。

這種形式就是閉包。

裝飾器

裝飾器本身也是一個函式 ,作用是為現有存在的函式,在不改變函式的基礎上去增加一些功能進行裝飾。

裝飾器實際上就是一個函式 ,這個函式以閉包的形式定義使用 @裝飾器函式名 形式來裝飾。

比如現在一個專案中,有很多函式 ,由於專案越來越大,功能越來越多,導致程式越來越慢。

其中一個功能函式功能,實現一百萬次的累加。

def my_count():
    s = 0
    for i in range(1000001):
        s += i
    print('sum : ', s)複製程式碼

現在想計算一下函式的執行時間,如何解決?如何能應用到所有函式上?

解決辦法:

import time

def count_time(func):
    def wrapper():      #wrapper 裝飾
        start = time.time()
        func()
        end = time.time()
        print('共計執行:%s 秒'%(end - start)) # 使用%d顯示,取整後是0秒,因為不到一秒
    return wrapper

@count_time     # 這實際就相當於解決方法3中的 my_count = count_tiem(my_count)
def my_count():
    s = 0
    for i in range(10000001):
        s += i
    print('sum : ', s)

my_count()複製程式碼

這樣實現的好處是,定義好了閉包函式後。只需要通過 @xxx 形式的裝飾器語法,將 @xxx 加到要裝飾的函式前即可。

萬能裝飾器

 def setFunc(func):
        def wrapper(*args, **kwargs):   # 接收不同的引數
            print('wrapper context')
            return func(*args, *kwargs) # 再原樣傳回給被裝飾的函式

        return wrapper

    @setFunc
    def show(name, age):
        print(name,age)

    show('tom',12)
複製程式碼

通過可變引數和關鍵字引數來接收不同的引數型別。

類實現裝飾形式

通過類的定義也可以實現裝飾器形式。

在類中通過使用 __init__ 和 __call__方法來實現

    class Test(object):
        # 通過初始化方法,將要被裝飾的函式傳進來並記錄下來
        def __init__(self, func):
            self.__func = func
        # 重寫 __call__ 方法來實現裝飾內容
        def __call__(self, *args, **kwargs):
            print('wrapper context')
            self.__func(*args, **kwargs)


    # 實際通過類的魔法方法call來實現
    @Test  # --> show = Test(show) show由原來引用函式,裝飾後變成引用Test裝飾類的物件
    def show():
        pass


    show()  # 物件呼叫方法,實際上是呼叫魔法方法call,實現了裝飾器複製程式碼


相關文章