函式裝飾器和閉包

2021-10-08 01:32:14 字數 4202 閱讀 8050

裝飾器是可呼叫的物件,其引數是另乙個函式(被裝飾的函式)。

裝飾器可能會處理被裝飾的函式,然後把它返回,或者將其替換成另乙個函式或可呼叫物件。

#裝飾器通常把函式替換成另乙個函式

>>

>

defdeco

(func):.

..definner()

:...

print

('running inner()').

..return inner #➊ ..

.>>

> @deco ..

.def

target()

:#➋ ..

.print

('running target()').

..>>

> target(

)#➌

running inner(

)>>

> target #➍

<

locals

>

.inner at 0x10063b598

>

➊deco返回inner函式物件

➋使用deco裝飾target

➌ 呼叫被裝飾的target,實際執行的是inner

➍審查物件,發現target現在是inner的引用。

嚴格來說,裝飾器只是語法糖。如前所示,裝飾器可以像常規的可呼叫物件那樣呼叫,其引數是另乙個函式。有時,這樣做更方便,尤其是做元程式設計(在執行時改變程式的行為)時。

綜上,裝飾器的一大特性是,能把被裝飾的函式替換成其他函式。第二個特性是,裝飾器在載入模組時立即執行

裝飾器的乙個關鍵特性是,它們在被裝飾的函式定義之後立即執行。這通常是在匯入時(即python載入模組時)。

registry =

#➊ def

register

(func)

:#➋

print

('running register(%s)'

% func)

#➌ #➍

return func #➎

@register #➏

deff1()

:print

('running f1()')

@register

deff2()

:print

('running f2()'

)deff3(

):#➐ print

('running f3()'

)def

main()

:#➑

print

('running main()'

)print

('registry ->'

, registry)

f1() f2(

) f3(

)if __name__==

'__main__'

: main(

)#➒

➊ registry儲存被@register裝飾的函式引用。

➋ register的引數是乙個函式。

➌ 為了演示,顯示被裝飾的函式。

➍ 把func存入registry。

➎ 返回func:必須返回函式;這裡返回的函式與通過引數傳入的一樣。

➏ f1和f2被@register裝飾。

➐ f3沒有裝飾。

➑ main顯示registry,然後呼叫f1()、f2()和f3()。

➒ 只有把registration.py當作指令碼執行時才呼叫main()。

執行上述程式,輸出如下

running register(

>

)running register(

>

)running main(

)registry -

>

[>

,>

]running f1(

)running f2(

)running f3(

)

register在模組中其他函式之前執行(兩次)。呼叫register時,傳給它的引數是被裝飾的函式,例如。

registry中有兩個被裝飾函式的引用:f1和f2。這兩個函式,以及f3,只在main明確呼叫它們時才執行。

>>

>

import registration

running register(

>

) running register(

>

)

檢視register的值,輸出如下

>>

> registration.registry

[>

,>

]

由此可知:函式裝飾器在匯入模組時立即執行,而被裝飾的函式只在明確呼叫時執行。這突出了python程式設計師所說的匯入時和執行時之間的區別。

使用註冊裝飾器可以改進之前的電商**折扣示例。

回顧一下,之前示例的主要問題是,定義體中有函式的名稱,但是best_promo用來判斷哪個折扣幅度最大的promos列表中也有函式名稱。這種重複是個問題,因為新增策略函式後可能會忘記把它新增到promos列表中,導致best_promo忽略新策略,而且不報錯,為系統引入了不易察覺的缺陷。示例7-3使用註冊裝飾器解決了這個問題。

#promos列表中的值使用promotion裝飾器填充

promos =

#➊ def

promotion

(promo_func)

:#➋

return promo_func

@promotion #➌

deffidelity

(order)

:"""為積分為1000或以上的顧客提供5%折扣"""

return order.total()*

.05if order.customer.fidelity >=

1000

else0

@promotion

defbulk_item

(order)

:"""單個商品為20個或以上時提供10%折扣"""

discount =

0for item in order.cart:

if item.quantity >=

20:

discount += item.total()*

.1return discount

@promotion

deflarge_order

(order)

:"""訂單中的不同商品達到10個或以上時提供7%折扣"""

distinct_items =

iflen

(distinct_items)

>=10:

return order.total()*

.07return

0def

best_promo

(order)

:#➍

"""選擇可用的最佳折扣

"""return

max(promo(order)

for promo in promos)

➊ promos列表起初是空的。

➋ promotion把promo_func新增到promos列表中,然後原封不動地將其返回。

➌ 被@promotion裝飾的函式都會新增到promos列表中。

➍ best_promos無需修改,因為它依賴promos列表

與之前給出的方案相比,這個方案有幾個優點。

• **策略函式無需使用特殊的名稱(即不用以_promo結尾)。

• @promotion裝飾器突出了被裝飾的函式的作用,還便於臨時禁用某個**策略:只需把裝飾器注釋掉。

• **折扣策略可以在其他模組中定義,在系統中的任何地方都行,只要使用@promotion裝飾即可。

多數裝飾器會修改被裝飾的函式。通常,它們會定義乙個內部函式,然後將其返回,替換被裝飾的函式。使用內部函式的**幾乎都要靠閉包才能正確運作

閉包函式和裝飾器

目錄 二 閉包函式的應用 裝飾器閉包 閉是封閉 函式內部巢狀函式 包是包含,閉包是指該內部函式對外部作用域而非全域性作用域的變數的引用。為函式傳參的方式一 使用引數的形式def func x print x func 1 為函式傳參的方式二 包給函式def outter x x 2 def inne...

閉包函式和裝飾器

目錄裝飾器 閉包即函式內部函式對外部作用域而非全域性作用域的引用,說白了就是將函式內部的變數拿到全域性來使用,還不會修改區域性變數的值 def outter x 5 def inner return x return inner f outter x f 1 print f print x 5 6使...

閉包函式和裝飾器

閉包函式 作用域關係在函式定義階段時就已經固定死了,與呼叫位置無關 即 在任意位置呼叫函式都需要跑到定義函式時尋找作用域關係 def f1 x 1 def inner print x x最後還是等於1,因為只看定義階段,return inner func f1 def f2 x 111111 fun...