< Python全景系列-9 > Python 裝飾器:優雅地增強你的函數和類

来源:https://www.cnblogs.com/xfuture/archive/2023/05/31/17445659.html
-Advertisement-
Play Games

裝飾器在 Python 中扮演了重要的角色,這是一種精巧的語言特性,讓我們能夠修改或增強函數和類的行為,無需修改它們的源代碼。這篇文章將深入探討裝飾器的所有相關主題,包括裝飾器的基礎知識、實現與使用、工作原理,以及通過實際例子學習裝飾器的獨特用法。 ...


歡迎來到我們的系列博客《Python全景系列》第九篇!在這個系列中,我們將帶領你從Python的基礎知識開始,一步步深入到高級話題,幫助你掌握這門強大而靈活的編程語法。無論你是編程新手,還是有一定基礎的開發者,這個系列都將提供你需要的知識和技能。

** 裝飾器在 Python 中扮演了重要的角色,這是一種精巧的語言特性,讓我們能夠修改或增強函數和類的行為,無需修改它們的源代碼。這篇文章將深入探討裝飾器的所有相關主題,包括裝飾器的基礎知識、實現與使用、工作原理,以及通過實際例子學習裝飾器的獨特用法。**

Python 裝飾器深入探討

在 Python 中,裝飾器提供了一種簡潔的方式,用來修改或增強函數和類的行為。裝飾器在語法上表現為一個前置於函數或類定義之前的特殊標記:

@simple_decorator
def hello_world():
    print("Hello, world!")

在這個例子中,simple_decorator 是一個裝飾器,它作用於下方的 hello_world 函數。裝飾器在概念上就像一個包裝器,它可以在被裝飾的函數執行前後插入任意的代碼,進而改變被裝飾函數的行為。

參數化裝飾器

我們還可以進一步將裝飾器參數化,這讓裝飾器的行為更具靈活性。比如,我們可以定義一個裝飾器,讓它在函數執行前後列印自定義的消息:

def message_decorator(before_message, after_message):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(before_message)
            result = func(*args, **kwargs)
            print(after_message)
            return result
        return wrapper
    return decorator

@message_decorator("Start", "End")
def hello_world():
    print("Hello, world!")

在這個例子中,message_decorator 是一個參數化裝飾器,它接受兩個參數,分別代表函數執行前後要列印的消息。

理解裝飾器的工作原理

在 Python 中,函數是第一類對象。這意味著函數和其他對象一樣,可以作為變數進行賦值,可以作為參數傳給其他函數,可以作為其他函數的返回值,甚至可以在一個函數裡面定義另一個函數。這個特性是實現裝飾器的基礎。

def decorator(func):
    def wrapper():
        print('Before function execution')
        func()
        print('After function execution')
    return wrapper

def hello_world():
    print('Hello, world!')

decorated_hello = decorator(hello_world)
decorated_hello()

在這個例子中,decorator 函數接收一個函數 hello_world 作為參數,並返回了一個新的函數 wrapped_func。這個新函數在 hello_world 函數執行前後分別列印一條消息。我們可以看到,裝飾器實際上是一個返回函數的函數。

函數簽名保持

預設情況下,裝飾器會“掩蓋”掉原函數的名字和文檔字元串。這是因為在裝飾器內部,我們返回了一個全新的函數。我們可以使用 functools.wraps 來解決這個問題:

import functools

def decorator(func):
    @functools.wraps(func)
    def wrapper():
        print('Before function execution')
        func()
        print('After function execution')
    return wrapper

@decorator
def hello_world():
    "Prints 'Hello, world!'"
    print('Hello, world!')

print(hello_world.__name__)
print(hello_world.__doc__)

這樣,使用裝飾器後的函數名和文檔字元串能夠保持不變。

Python 裝飾器的應用實例

裝飾器在實際的 Python 編程中有許多應用場景,比如日誌記錄、性能測試、事務處理、緩存、許可權校驗等。

一個常見的應用就是使用裝飾器進行日誌記錄:

import logging

def log_decorator(func):
    logging.basicConfig(level=logging.INFO)
    
    def wrapper(*args, **kwargs):
        logging.info(f'Running "{func.__name__}" with arguments {args} and kwargs {kwargs}')
        result = func(*args, **kwargs)
        logging.info(f'Finished "{func.__name__}" with result {result}')
        return result
    
    return wrapper

@log_decorator
def add(x, y):
    return x + y

這個裝飾器記錄了函數的名稱,函數調用的參數,以及函數返回的結果。

裝飾器鏈

Python 允許我們將多個裝飾器應用到一個函數上,形成一個裝飾器鏈。例如,我們可以同時應用日誌裝飾器和性能測試裝飾器:

import time
import logging
from functools import wraps

def log_decorator(func):
    logging.basicConfig(level=logging.INFO)
    
    @wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f'Running "{func.__name__}" with arguments {args} and kwargs {kwargs}')
        result = func(*args, **kwargs)
        logging.info(f'Finished "{func.__name__}" with result {result}')
        return result

    return wrapper

def timer_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f'Function "{func.__name__}" took {end_time - start_time} seconds to run.')
        return result

    return wrapper

@log_decorator
@timer_decorator
def add(x, y):
    time.sleep(2)
    return x + y

在這個例子中,@log_decorator@timer_decorator 兩個裝飾器被同時應用到 add 函數上,它們分別負責記錄日誌和測量函數運行時間。

One More Thing: 自動註冊裝飾器

一個有趣的裝飾器應用是自動註冊。這個裝飾器會在裝飾函數時自動將函數添加到一個列表或字典中,這樣我們就可以在程式的其他地方訪問到這個列表或字典,知道有哪些函數被裝飾過。

# 裝飾器將函數註冊到一個列表中
def register_decorator(func_list):
    def decorator(func):
        func_list.append(func)
        return func
    return decorator

# 自動註冊函數
registered_functions = []
@register_decorator(registered_functions)
def foo():
    pass

@register_decorator(registered_functions)
def bar():
    pass

print(registered_functions)  # 輸出: [<function foo at 0x10d38d160>, <function bar at 0x10d38d1f0>]

這個裝飾器可以用於自動註冊路由、插件系統、命令行參數處理等場景,能夠大大提高代碼的靈活性和可擴展性。

總結

Python 裝飾器是一種強大的工具,它可以讓我們更有效地管理和組織代碼。希望通過這篇文章,你能夠更深入地理解裝飾器的工作原理和用法,從而在你的項目中更好地使用裝飾器。

如有幫助,請多關註
個人微信公眾號:【Python全視角】
TeahLead_KrisChang,10+年的互聯網和人工智慧從業經驗,10年+技術和業務團隊管理經驗,同濟軟體工程本科,復旦工程管理碩士,阿裡雲認證雲服務資深架構師,上億營收AI產品業務負責人。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在Java中,序列化(Serialization)是指將對象的狀態轉換為位元組流的過程,以便將其保存到文件、在網路中傳輸或持久化到資料庫中。而反序列化(Deserialization)則是將位元組流轉換回對象的過程,恢復對象的狀態。 序列化和反序列化主要用於以下場景: 1. 對象持久化:通過序列化,可以 ...
  • > 內容摘自我的學習網站:topjavaer.cn Redis連環40問,絕對夠全! ## Redis是什麼? Redis(`Remote Dictionary Server`)是一個使用 C 語言編寫的,高性能非關係型的鍵值對資料庫。與傳統資料庫不同的是,Redis 的數據是存在記憶體中的,所以讀寫 ...
  • > 本文首發於公眾號:Hunter後端 > 原文鏈接:[Python連接es筆記四之創建和刪除操作](https://mp.weixin.qq.com/s/ZCe0JT9TDEiZI7M5dxC9qA) 這一篇筆記介紹一下索引和數據的創建和刪除。 其實對於索引來說,如果可以接觸到 kibana 的話 ...
  • > 源碼都背下來了,你給我看這 **我是 javapub,一名 `Markdown` 程式員從👨‍💻,八股文種子選手。** **面試官: 你好,我看到你的簡歷上寫著你熟悉 Java 中的 "synchronized" 關鍵字。你能給我講講它的作用嗎?** **候選人:** 當然,"synchro ...
  • # 1. Netty總體結構 ## 1.1 Netty簡介 ​ Netty是一款用於創建高性能網路應用程式的高級框架。它的基於 Java NIO 的非同步的和事件驅動的實現,保證了高負載下應用程式性能的最大化和可伸縮性。 ​ 其次,Netty 也包含了一組**設計模式**,將應用程式邏輯從網路層解耦, ...
  • 1. 多線程程式在一個核的CPU運行 ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35902537/1685453577663-714d9c16-e8a3-4828-bb86-86dfa10c8e52.png#averageHue=%23f ...
  • ![](https://img2023.cnblogs.com/other/1218593/202305/1218593-20230531145646615-374710580.png) ## **1.簡介** Grep Console是一款方便開發者對idea控制台輸出日誌進行個性化管理的插件。 ...
  • # 基於 Web 實現 m3u8 視頻播放的簡單應用示例 ## 實現思路 將視頻(MP4 等)轉換為 M3U8 視頻的服務,可以按照以下步驟進行操作: 1. 將視頻(MP4 等)轉換為 M3U8:在服務中,使用適當的工具(如 FFmpeg)將接收到的視頻(MP4 等)轉換為 M3U8 格式。這將生成 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...