Python 裝飾器入門的四個簡單示例
裝飾器是 Python 中非常強大且實用的功能之一,它可以幫助我們更加優雅地編寫代碼。裝飾器本質上是一個函數,它可以修改或增強其他函數的行為,而無需改變原函數的代碼。今天,我們就來一步步學習 Python 裝飾器的基礎知識,并通過幾個簡單的示例來加深理解。
一、基礎概念
1.什么是裝飾器?
裝飾器是一個函數,它接受一個函數作為參數,并返回一個新的函數。這個新的函數通常會在執行原函數之前或之后添加一些額外的功能。
2.裝飾器的基本語法
裝飾器的基本語法使用 @ 符號。例如:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
輸出:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
在這個例子中,my_decorator 是一個裝飾器,它接受 say_hello 函數作為參數,并返回一個新的 wrapper 函數。當我們調用 say_hello() 時,實際上是調用了 wrapper() 函數。
二、帶參數的裝飾器
有時候,我們希望裝飾器能夠接受參數。這可以通過再嵌套一層函數來實現。
示例:帶參數的裝飾器
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
輸出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
在這個例子中,repeat 是一個帶參數的裝飾器,它接受一個整數 num_times 作為參數,并返回一個真正的裝飾器 decorator。decorator 再次接受 greet 函數作為參數,并返回 wrapper 函數。wrapper 函數會多次調用 greet 函數。
三、帶參數的被裝飾函數
如果被裝飾的函數本身帶有參數,我們需要在 wrapper 函數中傳遞這些參數。
示例:帶參數的被裝飾函數
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
def add(a, b):
print(f"Adding {a} and ")
return a + b
result = add(3, 5)
print(f"Result: {result}")
輸出:
Something is happening before the function is called.
Adding 3 and 5
Something is happening after the function is called.
Result: 8
在這個例子中,add 函數接受兩個參數 a 和 b。wrapper 函數通過 *args 和 **kwargs 接受這些參數,并將它們傳遞給 add 函數。
四、使用 functools.wraps
為了保持被裝飾函數的元數據(如名稱、文檔字符串等),我們可以使用 functools.wraps。
示例:使用 functools.wraps
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
def add(a, b):
"""Add two numbers."""
print(f"Adding {a} and ")
return a + b
print(add.__name__)
print(add.__doc__)
輸出:
add
Add two numbers.
在這個例子中,@wraps(func) 保留了 add 函數的元數據,使得 add.__name__ 和 add.__doc__ 仍然有效。
實戰案例:日志記錄裝飾器
假設我們有一個應用程序,需要記錄每個函數的調用時間和返回值。我們可以使用裝飾器來實現這一點。
示例:日志記錄裝飾器
import time
from functools import wraps
def log_function_call(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Function {func.__name__} was called with arguments {args} and {kwargs}.")
print(f"Return value: {result}")
print(f"Time taken: {elapsed_time:.4f} seconds")
return result
return wrapper
@log_function_call
def compute_sum(a, b):
"""Compute the sum of two numbers."""
time.sleep(1) # Simulate a delay
return a + b
result = compute_sum(3, 5)
輸出:
Function compute_sum was called with arguments (3, 5) and {}.
Return value: 8
Time taken: 1.0002 seconds
在這個例子中,log_function_call 裝飾器記錄了 compute_sum 函數的調用時間、參數和返回值。time.sleep(1) 模擬了一個延遲,以便我們可以看到時間記錄的效果。
總結
本文介紹了 Python 裝飾器的基礎知識,包括裝飾器的基本概念、帶參數的裝飾器、帶參數的被裝飾函數以及如何使用 functools.wraps 保留元數據。通過四個簡單的示例,我們逐步深入理解了裝飾器的工作原理和應用場景。最后,我們通過一個實戰案例展示了如何使用裝飾器來記錄函數的調用信息。