python基礎-閉包函數和裝飾器

閉包函數和裝飾器

閉包函數

  • 概念:在函數中(嵌套)定義另外一個函數時,內部函數引用了外層函數的名字。
  • 特性
    • 閉包函數必須在函數內部定義
    • 閉包函數可引用外層函數的名字

閉包函數是函數嵌套、函數對象、名稱空間與做用域結合體html

閉包中被內部函數引用的變量,不會由於外部函數結束而被釋放掉,而是一直存在內存中,直到內部函數被調用結束python

# 閉包函數的定義
def func(y):
    x = 100
    def inner():        # 此處的inner 就是閉包函數
        print(x)
        print(y)
    return inner

# 閉包函數的調用
inner = func(1000)
inner()
  • 應用:爲了給裝飾器的使用作準備

裝飾器

  • 概念:在不修改被裝飾對象源代碼與調用方式的前提下,添加新的功能。簡單來講,就是給其餘函數增長額外功能的函數
  • 裝飾器必須遵循「開放封閉」原則
    • 開放:對函數功能的添加是開放的
    • 關閉:對函數功能的修改是封閉的
  • 必備條件
    • 不能改變被裝飾對象源代碼
    • 爲被裝飾對象添加新的功能
    • 不能改變被裝飾對象調用方式
  • 使用裝飾器,能夠解決代碼冗餘問題,提升代碼的可擴展性
  • 應用:統計時間、登陸認證

說了這多理論,上代碼。閉包

需求:統計下載媒體文件的時間(簡易版)app

# 需求: 統計下載電影的時間。
# 通常咱們實現這個功能的方式以下
import time


def download_movie():
    """
    下載電影功能
    :return: None
    """
    print("電影下載開始了……")
    time.sleep(2)  # 模擬電影下載2 秒
    print("下載完成")


start_time = time.time()  # 獲取當前時間戳
download_movie()  # 下載電影
end_time = time.time()  # 獲取當前時間戳
print(f"下載時間:{end_time-start_time}")

以上代碼確實實現了統計下載時間的功能,問題來了,若是我有多個類型文件下載函數,都須要統計時間呢?難道展示咱們的「CV大法」?估計隔天就要被掃地出門了……函數

就沒有什麼辦法能夠解決了嗎?固然有啦!先看代碼url

# 定義一個裝飾器
def time_record(func):
    """
    新增統計時間功能
    :param func: 使用該功能的函數對象
    :return: inner 函數對象
    """

    def inner():
        # 統計開始
        start_time = time.time()
        func()  # func()  ----→ download_movie() or download_music()
        end_time = time.time()
        # 統計結束,打印統計時間
        print(f"下載時間:{end_time-start_time}")

    return inner


def download_movie():
    """
    模擬下載電影
    :return: None
    """
    print("電影下載開始了……")
    time.sleep(2)
    print("下載完成!")


def download_music():
    """
    模擬下載音樂
    :return: None
    """
    print("歌曲下載開始了……")
    time.sleep(2)
    print("下載完成!")


# time_record(download_movie) 返回一個inner,將其賦值給download_movie
inner = time_record(download_movie)
inner()  # inner()  ----→  download_movie()

# time_record(download_music) 返回一個inner,將其賦值給download_music
inner1 = time_record2(download_music)
inner1()

以上代碼就是對裝飾器的引入,其中 time_record 就是裝飾器,download_moviedownload_music 就是被裝飾的函數對象。code

經過觀察🕵,以上被裝飾的函數對象是沒有返回值,且沒有參數的。問題來了,假如 download_movie 等被裝飾對象是有返回值和參數,那該咋整?htm

不說廢話🙊,繼續看代碼對象

# 定義一個裝飾器
def time_record(func):
    """
    新增統計時間功能
    :param func: 使用該功能的函數對象
    :return: inner 函數對象
    """

    def inner(*args, **kwargs):
        # 統計開始
        start_time = time.time()
        # func()  ----→ download_movie();定義變量接收download_movie() 的返回值
        # 定義可變長位置參數,用於接收任意類型的位置參數,甚至無參均可
        # 同理,定義可變長關鍵字參數,用於接收任意類型的關鍵字參數,甚至是無參都行
        res = func(*args, **kwargs)
        end_time = time.time()
        # 統計結束,打印統計時間
        print(f"下載時間:{end_time-start_time}")
        return res  # 將download_movie() 的返回值返回

    return inner


def download_movie(url):
    """
    模擬下載電影
    :return: None
    """
    print(f"{url}的電影下載開始了……")
    time.sleep(2)
    print("下載完成!")
    return "海賊王.mp4"


def download_music(url, name):
    """
    模擬下載音樂
    :return: None
    """
    print(f"{url}中的歌曲下載開始了……")
    time.sleep(2)
    print(f"{name}.mp3 下載完成!")


download_movie = time_record(download_movie)
# 接收download_movie()的返回值
res = download_movie("https://www.cnblogs.com/xiaoyuanqujing/p/11636160.html")
print(res) # 打印返回值

download_music = time_record(download_music)
download_music("https://music.163.com/", name="煙火裏的塵埃")  # 傳入一個位置參數和一個關鍵字參數

細心如你,裝飾器定義好了,可是每次調用都特別麻煩,有沒有一種比較便捷的調用方式呢?blog

噹噹噹當!固然有,那就是裝飾器的語法糖,聽起來就感受使用很是甜,咋用呢?繼續看代碼(因爲以前代碼已經實現了裝飾器,這裏就不重複展現了,只實現語法糖的代碼

@time_record  # 這就是裝飾器time_record 的語法糖
def download_movie(url):
    """
    模擬下載電影
    :return: None
    """
    print(f"{url}的電影下載開始了……")
    time.sleep(2)
    print("下載完成!")
    return "海賊王.mp4"


@time_record  # 這就是裝飾器time_record 的語法糖
def download_music(url, name):
    """
    模擬下載音樂
    :return: None
    """
    print(f"{url}中的歌曲下載開始了……")
    time.sleep(2)
    print(f"{name}.mp3 下載完成!")


res = download_movie("https://www.cnblogs.com/xiaoyuanqujing/p/11636160.html")
print(res)
download_music("https://music.163.com/", name="煙火裏的塵埃")  # 傳入一個位置參數和一個關鍵字參數
  • 裝飾器語法糖

    • 顧名思義,這個關鍵字只屬於裝飾器
    • 關鍵字@
    • 使用方法:@ 裝飾器名稱

    注意:在使用時,裝飾器必須在被裝飾對象以前定義

  • 裝飾器終極模板

    # func 表示須要裝飾的函數對象
    def wrapper(func):
        def inner(*args, **kwargs):  # 接收不固定參數
            # 調用func 以前增長的新功能
            res = func(*args, **kwargs)  # 調用被裝飾對象,獲得返回值
            # 調用func 以後增長的新功能
            return res  # 將返回值返回
    
        return inner
    
    
    @wrapper
    def func1():
        pass
    
    
    # 使用裝飾器語法糖後,直接調用函數
    func1()
相關文章
相關標籤/搜索