Python 閉包

在提到閉包之前,我們需要對函數(shù)做一些梳理:
函數(shù)的局部參數(shù)是無法保存的,每次執(zhí)行函數(shù)都是將參數(shù)初始化并執(zhí)行

閉包可以使函數(shù)擁有自己的環(huán)境上下文,在其中保存執(zhí)行后的信息。使函數(shù)表現(xiàn)的像一個對象,豐富了函數(shù)的功能。

0、引言


我們現(xiàn)在要實(shí)現(xiàn)一個具有緩存功能的函數(shù),"如果后續(xù)傳入相同的參數(shù),則不再計算,直接返回結(jié)果"。

如果是在對象中,這種緩存非常簡單就能實(shí)現(xiàn)。但是在函數(shù)中則異常麻煩:

  • 1 幻想中的寫法, [實(shí)際上并不能起到作用]
import time


def cache_sum(a, b):
    """這是一個具有緩存功能的求和函數(shù)"""
    cache = {}

    key = str(a) + str(b)
    if key in cache:            # 1. 具有緩存結(jié)果,則直接返回
        return cache.get(key)
    else:
                                # 2. 計算
        time.sleep(1)
        ret = a + b
        cache[key] = ret        # 3. 將結(jié)果存儲進(jìn)緩存

使用cache_sum

if __name__ == '__main__':
    print(datetime.datetime.now())  # 2020-05-04 09:13:13.239913
    cache_sum(1, 5)
    print(datetime.datetime.now())  # 2020-05-04 09:13:14.242900
    cache_sum(1, 5)
    print(datetime.datetime.now())  # 2020-05-04 09:13:15.246474

可以看出,在函數(shù)中cache = {}作為局部變量,每次都會被初始化,根本起不到作用 [ 兩次執(zhí)行都耗時一秒,說明緩存沒有起作用]。

    1. 改進(jìn)寫法
def cache_sum(a, b):
    """這是一個具有緩存功能的求和函數(shù)"""

    key = str(a) + str(b)
    if key in cache:  # 1. 這里的 cache 由使用者提供
        return cache.get(key)
    else:
        # 2. 計算
        time.sleep(1)
        ret = a + b
        cache[key] = ret  # 3. 將結(jié)果存儲進(jìn)緩存

使用 cache_sum

if __name__ == '__main__':
    cache = {}

    print(datetime.datetime.now())  # 2020-05-04 09:19:43.842472
    cache_sum(1, 5)
    print(datetime.datetime.now())  # 2020-05-04 09:19:44.846783
    cache_sum(1, 5)
    print(datetime.datetime.now())  # 2020-05-04 09:19:44.846864

[ 第二次沒有耗時,說明緩存起作用了]
在這種寫法中,我們執(zhí)行cache_sum還需要提供 cache = {}這樣一個變量,這樣會引起很多問題:

  • 使用者會不會忘記提供 cache = {}?

  • 使用者會不會在外部修改 cache?
    這些都是無法預(yù)料的問題。顯然不能作為這個問題的解決方式

    1. 使用閉包完成
import time


def as_cache_sum():
    """返回一個具有緩存功能的函數(shù)

    你應(yīng)該這樣使用它

    cache_sum = as_cache_sum()
    ret = cache_sum(1,4)
    print(ret)  # 5
    """
    cache = {}

    def cache_sum(a, b):
        key = str(a) + str(b)
        if key in cache:  # 1. 具有緩存結(jié)果,則直接返回
            return cache.get(key)
        else:
            # 2. 計算
            time.sleep(1)
            ret = a + b
            cache[key] = ret  # 3. 將結(jié)果存儲進(jìn)緩存

    return cache_sum

第二種寫法相比,這里提供變量cache是指外部函數(shù)中完成的,而使用者是接觸不到這個變量的。

細(xì)細(xì)的品,你是不是有點(diǎn)明白閉包的這個


一、使用“閉包”

“閉包”的本質(zhì)是函數(shù)的嵌套定義,即在函數(shù)內(nèi)部再定義函數(shù)。

在下面這個函數(shù)中

 def make_averager():
        """返回一個計算平均值的函數(shù)"""
        series = []

        def averager(new_value):
            series.append(new_value)

            total = sum(series)
            return total / len(series)

        return averager

調(diào)用 make_averager 時,返回一個 averager 函數(shù)對象。每次調(diào)用 averager 時,它會 把參數(shù)添加到系列值中,然后計算當(dāng)前平均值。

averager = make_averager()
print(averager(10))  # 10
 
print(averager(12))  # 11

print(averager(14))  # 12

在上述函數(shù)中make_averager并不包含真正的執(zhí)行邏輯,它只做了兩件事情

  • 為真正的執(zhí)行函數(shù)提供環(huán)境上下文
  • 返回執(zhí)行函數(shù)

averager函數(shù)才是真正執(zhí)行邏輯的地方,它使用了make_averager為它提供了series環(huán)境變量,averagerseries的修改會被保存起來(伴隨著averager,直到其被銷毀)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容