python 閉包,裝飾器

1. 閉包

在函數(shù)內(nèi)部再定義一個函數(shù),并且內(nèi)部函數(shù)用到了外部函數(shù)作用域里的變量(enclosing),那么將這個內(nèi)部函數(shù)以及用到的外部函數(shù)內(nèi)的變量一起稱為閉包(Closure)
基本格式:

def 外部函數(shù)名():
  內(nèi)部函數(shù)需要的變量
  def 內(nèi)部函數(shù)名()
    引用外部的變量
  return 內(nèi)部函數(shù)

  • 注意:
    函數(shù)的作用域關(guān)系在函數(shù)定義階段就已經(jīng)固定,與調(diào)用位置無關(guān)。
    無論函數(shù)在何處調(diào)用,都需要回到定義階段去找對應(yīng)的作用域關(guān)系。
    閉包中不要引用外部函數(shù)中任何循環(huán)變量或后續(xù)會發(fā)生變化的變量(延遲計算)

2. 裝飾器

2.1 裝飾器語法糖

@ 符號就是裝飾器的語法糖
它放在一個函數(shù)開始定義的地方,它就像一頂帽子一樣戴在這個函數(shù)的頭上。和這個函數(shù)綁定在一起。在我們調(diào)用這個函數(shù)的時候,第一件事并不是執(zhí)行這個函數(shù),而是將這個函數(shù)做為參數(shù)傳入它頭頂上這頂帽子,這頂帽子我們稱之為裝飾函數(shù)裝飾器

裝飾器的使用方法很固定:
(1) 先定義一個裝飾函數(shù)(帽子)(也可以用類、偏函數(shù)實(shí)現(xiàn))
(2) 再定義你的業(yè)務(wù)函數(shù)、或者類(人)
(3) 最后把這頂帽子帶在這個人頭上

2.2 裝飾器無參數(shù)

格式:

def 裝飾器外部函數(shù)(func):      #func 就是被裝飾函數(shù)  
    def裝飾器內(nèi)部函數(shù)(*args, **kwargs):   #裝飾器內(nèi)部函數(shù)是一個閉包,它使用了外部函數(shù)的變量func裝飾函數(shù)的功能
        裝飾器的功能
        return func(*args, **kwargs)   #執(zhí)行被裝飾函數(shù)并返回被裝飾函數(shù)的返回值
    return 返回裝飾器內(nèi)部函數(shù)名      #注!返回裝飾器內(nèi)部函數(shù)名而不是裝飾器內(nèi)部函數(shù)

@裝飾器外部函數(shù)名   #將被裝飾函數(shù)傳入裝飾器,返回裝飾器內(nèi)部函數(shù)引用
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
    被裝飾函數(shù)的功能
    return 被裝飾函數(shù)的返回值

接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù))    #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值)   #打印一下接收的返回值

2.4 裝飾器帶參數(shù)

根據(jù)功能需求,在原來的裝飾器外面再加一層函數(shù)
格式:

def 需求函數(shù)(需求函數(shù)參數(shù)):
    def 裝飾器外部函數(shù)(func):
        def 裝飾器內(nèi)部函數(shù)(*args, **kwargs):
            裝飾器的功能
            return func(*args, **kwargs)
        return 裝飾器內(nèi)部函數(shù)
    return 裝飾器外部函數(shù)


@需求函數(shù)('需求函數(shù)參數(shù)')  # 等價于 被裝飾函數(shù) = 需求函數(shù)('需求函數(shù)參數(shù)')(被裝飾函數(shù)) ,即先執(zhí)行需求函數(shù)('需求函數(shù)參數(shù)'),返回裝飾
                         #(接上一行的解釋)器外部函數(shù)引用(真正的裝飾器),再用裝飾器外部函數(shù)裝飾被裝飾函數(shù),返回裝飾器內(nèi)部函數(shù)

def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
    被裝飾函數(shù)的功能
    return 被裝飾函數(shù)的返回值

接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù))    #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值)   #打印一下接收的返回值

2.5 使用@wraps

當(dāng)打印被裝飾函數(shù)函數(shù)名時,因為被裝飾函數(shù)是在裝飾器內(nèi)部函數(shù)調(diào)用所以顯示裝飾器內(nèi)部函數(shù)名,想打印被裝飾函數(shù)名時可以使用functools模塊的wraps裝飾器解決這個問題
格式:

from functools import wraps
def 需求函數(shù)(需求函數(shù)參數(shù)):
    def 裝飾器外部函數(shù)(func):
        @wraps(func)
        def 裝飾器內(nèi)部函數(shù)(*args, **kwargs):
            裝飾器的功能
            return func(*args, **kwargs)
        return 裝飾器內(nèi)部函數(shù)
    return 裝飾器外部函數(shù)


@需求函數(shù)('需求函數(shù)參數(shù)')  # 等價于 被裝飾函數(shù) = 需求函數(shù)('需求函數(shù)參數(shù)')(被裝飾函數(shù)) ,即先執(zhí)行需求函數(shù)('需求函數(shù)參數(shù)'),返回裝飾
                         #(接上一行解釋)器外部函數(shù)引用(真正的裝飾器),再用裝飾器外部函數(shù)裝飾被裝飾函數(shù),返回裝飾器內(nèi)部函數(shù)

def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
    被裝飾函數(shù)的功能
    return 被裝飾函數(shù)的返回值

接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù))    #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值)   #打印一下接收的返回值

2.6 多個裝飾器裝飾同一個函數(shù)

格式:

# 裝飾器1
def 裝飾器外部函數(shù)(func):      #func 就是被裝飾函數(shù)  
    def裝飾器內(nèi)部函數(shù)(*args, **kwargs):   #裝飾器內(nèi)部函數(shù)是一個閉包,它使用了外部函數(shù)的變量func裝飾函數(shù)的功能
        裝飾器的功能
        return func(*args, **kwargs)   #執(zhí)行被裝飾函數(shù)并返回被裝飾函數(shù)的返回值
    return 返回裝飾器內(nèi)部函數(shù)名      #注!返回裝飾器內(nèi)部函數(shù)名而不是裝飾器內(nèi)部函數(shù)


# 裝飾器2
def 裝飾器外部函數(shù)(func):      #func 就是被裝飾函數(shù)  
    def裝飾器內(nèi)部函數(shù)(*args, **kwargs):   #裝飾器內(nèi)部函數(shù)是一個閉包,它使用了外部函數(shù)的變量func裝飾函數(shù)的功能
        裝飾器的功能
        return func(*args, **kwargs)   #執(zhí)行被裝飾函數(shù)并返回被裝飾函數(shù)的返回值
    return 返回裝飾器內(nèi)部函數(shù)名      #注!返回裝飾器內(nèi)部函數(shù)名而不是裝飾器內(nèi)部函數(shù)



@裝飾器1的外部函數(shù)名   #將被裝飾函數(shù)傳入裝飾器,返回裝飾器內(nèi)部函數(shù)引用
@裝飾器2的外部函數(shù)名   #將被裝飾函數(shù)傳入裝飾器,返回裝飾器內(nèi)部函數(shù)引用
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
    被裝飾函數(shù)的功能
    return 被裝飾函數(shù)的返回值

接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù))    #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值)   #打印一下接收的返回值

小結(jié):多個裝飾器的順序,包裝時,是從下往上的,也就是被裝飾函數(shù)頭上,哪個離得近就先包裝哪個

2.7 基于類實(shí)現(xiàn)的裝飾器

基于類裝飾器的實(shí)現(xiàn),必須實(shí)現(xiàn)__call____init__兩個內(nèi)置函數(shù)。

2.7.1 不帶參數(shù)的類裝飾器
  • __init__:接收被裝飾函數(shù)
  • __call__ :實(shí)現(xiàn)裝飾邏輯
    格式:
class 類裝飾器函數(shù)(object):
  def __init__(self, func):  #接收被裝飾函數(shù)
    self.func = func
 
  def __call__(self, *args, **kwargs):   #實(shí)現(xiàn)裝飾邏輯
    裝飾器函數(shù)的功能
    return self.func(*args, **kwargs)
 
@類裝飾器函數(shù)名
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
    被裝飾函數(shù)的功能

被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù)
2.7.2 帶參數(shù)的類裝飾器

帶參數(shù)和不帶參數(shù)的類裝飾器有很大的不同。

__init__:不再接收被裝飾函數(shù),而是接收傳入?yún)?shù)。
__call__ :接收被裝飾函數(shù),實(shí)現(xiàn)裝飾邏輯。

格式:

class 類裝飾器(object):
  def __init__(self, level='類裝飾器函數(shù)參數(shù)'):
    self.level = level
 
  def __call__(self, func): # 接受函數(shù)
    def 裝飾器內(nèi)部函數(shù)(*args, **kwargs):
      裝飾器的功能
      func(*args, **kwargs)
    return 裝飾器內(nèi)部函數(shù)名     #返回函數(shù)
 
@類裝飾器函數(shù)名(level='類裝飾器函數(shù)參數(shù)')
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
    被裝飾函數(shù)的功能

被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù)
最后編輯于
?著作權(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ù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

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