裝飾器在 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產品業務負責人。