python閉包和裝飾器

一、python函數(shù)作用域LEGB

python解釋器查找變量的原則(順序):
L→E→G→B
L:Local函數(shù)內部作用域
E:enclosing函數(shù)內部與內嵌函數(shù)之間
G:gobal全局作用域
B:build-in內置作用域

example:

value1 = 5
def my_func():
    value2 = 6
    print(id(value2))
    def in_func():
        a = max(value1, value2)
        print(a)
    return in_func

上面示例代碼中max函數(shù)為python的內建函數(shù),在in_func函數(shù)中,max這個變量python解釋器首先會在in_func這個函數(shù)中查找,即local函數(shù)內部作用域中查找,然后按順序再到E-G-B查找,最后在build-in內置作用域查找到。

二、閉包

1、概念:內部函數(shù)中對enclosing作用域的變量進行引用

上述例子中my_func函數(shù)中的內部函數(shù)in_func引用了enclosing作用域中的value2變量,這就叫做閉包。
由于函數(shù)執(zhí)行完后變量會被回收,當執(zhí)行完my_func這個函數(shù)后,value2變量會被回收。那么如果我們再次調用in_func這個函數(shù)時,需用引用到value2這個變量,是否會報錯?
看以下代碼:

example:

value1 = 5
def my_func():
    value2 = 6
    print(id(value2)) #打印value2的ID值
    def in_func():
        a = max(value1, value2)
        print(a)
    return in_func

f = my_func()    #my_func返回的是in_func,此時f指向in_func函數(shù)
f()    #f()相當于in_func(),調用了infunc函數(shù)
print(f.__closure__)

ouput:

501351024
6
(<cell at 0x0000000000AF8D98: int object at 0x000000001DE20270>,)

如果內部函數(shù)引用了enclosing作用域的變量,會將變量添加到函數(shù)__closure__的屬性中去。當再次查找這個變量時,會直接去函數(shù)__closure__的屬性中查找。我們可以看到代碼的輸出結果第一行即為value2的內存地址(501351024轉換為16進制:1DE20270)和__closure__屬性中的int對象的地址是一樣的( int object at 0x000000001DE20270)。

2、那么閉包到底有什么用呢?

我們來看下以下兩段代碼:

(1)、

def func_100(value):
    passline = 60
    if value >= passline:
        print('pass')
    else:
        print('failed')

def func_150(value):
    passline = 90
    if value >= passline:
        print('pass')
    else:
        print('failed')
func100(59)
func150(89)

(2)、

def set_passline(passline):
    def in_func(value):
        if value >= passline:
            print('pass')
        else:
            print('failed')
    return in_func
f_100 = set_passline(60) #passline=60被存儲在f_100的__closure__屬性中
f_150 = set_passline(90) #passline=90被存儲在f_150的__closure__屬性中
f_100(59)
f_150(89)

兩段代碼都能正確判斷滿分是100或150時,分數(shù)是否及格。但是第二段代碼,由于運用閉包,代碼復用性更高。

3、閉包更高級的應用

將閉包的概念中的變量變成函數(shù),同樣適用。即:內部函數(shù)中對enclosing作用域的函數(shù)進行引
example:

def my_sum(*args):
    return(sum(args))

def my_average(*args):
    return sum(args)/len(args)

def dec(func):
    def in_dec(*args):
        if len(args) == 0:    #對參數(shù)進行判斷,如果沒有參數(shù)直接返回0
            return 0
        for i in args:
            if not isinstance(i, int):    #對參數(shù)進行判斷,如果有一個參數(shù)不是int類型,直接返回0
                return 0
        return func(*args)    #此處對enclosing作用域的func函數(shù)進行引用
    return in_dec

evo_my_sum = dec(my_sum)
evo_my_average = dec(my_average)

PS:此處求和函數(shù)(my_sum)和求平均值函數(shù)(my_average)只對參數(shù)是否int類型進行判斷。
我們來分析下代碼:evo_my_sum = dec(my_sum)
由于函數(shù)dec返回的是in_dec函數(shù)
所以evo_my_sum指向的是in_dec函數(shù)
=》evo_my_sum = in_dec
=》evo_my_sum(1, 2, 3) = in_dec(1, 2, 3)
那么in_dec(1, 2, 3)evo_my_sum(1, 2, 3)會對求和的參數(shù)先進行判斷后,再調用my_sum函數(shù)。
同樣道理:
evo_my_average會對求平均的參數(shù)先進行判斷后,再調用my_average函數(shù)。
這樣就可以對參數(shù)統(tǒng)一進行判斷后再各自調用不同的函數(shù)。

三、裝飾器

裝飾器是用來裝飾函數(shù)的,它返回一個函數(shù)對象。語法:@Decorator
現(xiàn)在我們已經(jīng)定義了一個my_sum函數(shù)

def my_sum(*args):
    return(sum(args))

假設我們要增加my_sum函數(shù)的功能,比如,在函數(shù)調用前對參數(shù)進行一個判斷,但又不希望修改my_sum函數(shù)的定義,這種在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)。
我們要定義一個能判斷參數(shù)的decorator,如下:

def dec(func):
    def in_dec(*args):
        if len(args) == 0:
            return 0
        for i in args:
            if not isinstance(i, int):
                return 0
        return func(*args)
    return in_dec

按照python裝飾器的語法:

@dec
def my_sum(*args):
    return(sum(args))

這樣,調用my_sum函數(shù),不僅會運行my_sum函數(shù)本身,還會在運行my_sum函數(shù)前,對參數(shù)進行判斷。
把@dec放到my_sum函數(shù)的定義處,相當于執(zhí)行了語句:
my_sum = dec(my_sum)
看到這里是否覺得有點熟悉?
其實這個語句和上述閉包的高級應用是一樣的,只不過那部分把my_sum改成了evo_my_sum。
裝飾器到這里還差最后一步:
my_sum函數(shù)經(jīng)過裝飾后,由于dec(my_sum)返回的是in_dec函數(shù),此時my_sum.__name__屬性將從‘my_sum’變成‘in_dec’,為了保證此屬性不變,需在in_dec函數(shù)定義前加上語句@functools.wraps(func),保證my_sum.__name__屬性不變。否則,有些依賴函數(shù)簽名的代碼執(zhí)行就會出錯。一個完整的decorator的寫法如下:

import functools

def dec(func):
    @functools.wraps(func)
    def in_dec(*args):
        if len(args) == 0:
            return 0
        for i in args:
            if not isinstance(i, int):
                return 0
        return func(*args)
    return in_dec

decorator可以增強函數(shù)的功能,定義起來雖然有點復雜,但使用起來非常靈活和方便。
以上是觀看慕課網(wǎng)《python裝飾器》以及廖雪峰教程python裝飾器的總結。

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

相關閱讀更多精彩內容

  • 閉包: 在函數(shù)內部在定義一個函數(shù),并且這個函數(shù)用到了外面函數(shù)的變量,這個函數(shù)和用到的變量,稱為閉包. deftes...
    界面大叔閱讀 247評論 0 0
  • 前幾天學習python裝飾器時,看各種例子,上來就是一個嵌套函數(shù),還返回一個函數(shù)對象(返回內嵌函數(shù)),學得我是一臉...
    喵小琪閱讀 385評論 0 1
  • 本文為《爬著學Python》系列第四篇文章。從本篇開始,本專欄在順序更新的基礎上,會有不規(guī)則的更新。 在Pytho...
    SyPy閱讀 2,570評論 4 11
  • 曾經(jīng),在酷暑的夏天,寒冷的冬季,每天早上出門等公交車的時候我就會想要是有輛自行車就好了。這樣我就可以騎車在家和地鐵...
    葉子綠漫天閱讀 249評論 0 0

友情鏈接更多精彩內容