Python 裝飾器入門的四個(gè)簡(jiǎn)單示例
裝飾器是 Python 中非常強(qiáng)大且實(shí)用的功能之一,它可以幫助我們更加優(yōu)雅地編寫代碼。裝飾器本質(zhì)上是一個(gè)函數(shù),它可以修改或增強(qiáng)其他函數(shù)的行為,而無需改變?cè)瘮?shù)的代碼。今天,我們就來一步步學(xué)習(xí) Python 裝飾器的基礎(chǔ)知識(shí),并通過幾個(gè)簡(jiǎn)單的示例來加深理解。
一、基礎(chǔ)概念
1.什么是裝飾器?
裝飾器是一個(gè)函數(shù),它接受一個(gè)函數(shù)作為參數(shù),并返回一個(gè)新的函數(shù)。這個(gè)新的函數(shù)通常會(huì)在執(zhí)行原函數(shù)之前或之后添加一些額外的功能。
2.裝飾器的基本語法
裝飾器的基本語法使用 @ 符號(hào)。例如:
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.
在這個(gè)例子中,my_decorator 是一個(gè)裝飾器,它接受 say_hello 函數(shù)作為參數(shù),并返回一個(gè)新的 wrapper 函數(shù)。當(dāng)我們調(diào)用 say_hello() 時(shí),實(shí)際上是調(diào)用了 wrapper() 函數(shù)。
二、帶參數(shù)的裝飾器
有時(shí)候,我們希望裝飾器能夠接受參數(shù)。這可以通過再嵌套一層函數(shù)來實(shí)現(xiàn)。
示例:帶參數(shù)的裝飾器
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!
在這個(gè)例子中,repeat 是一個(gè)帶參數(shù)的裝飾器,它接受一個(gè)整數(shù) num_times 作為參數(shù),并返回一個(gè)真正的裝飾器 decorator。decorator 再次接受 greet 函數(shù)作為參數(shù),并返回 wrapper 函數(shù)。wrapper 函數(shù)會(huì)多次調(diào)用 greet 函數(shù)。
三、帶參數(shù)的被裝飾函數(shù)
如果被裝飾的函數(shù)本身帶有參數(shù),我們需要在 wrapper 函數(shù)中傳遞這些參數(shù)。
示例:帶參數(shù)的被裝飾函數(shù)
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
在這個(gè)例子中,add 函數(shù)接受兩個(gè)參數(shù) a 和 b。wrapper 函數(shù)通過 *args 和 **kwargs 接受這些參數(shù),并將它們傳遞給 add 函數(shù)。
四、使用 functools.wraps
為了保持被裝飾函數(shù)的元數(shù)據(jù)(如名稱、文檔字符串等),我們可以使用 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.
在這個(gè)例子中,@wraps(func) 保留了 add 函數(shù)的元數(shù)據(jù),使得 add.__name__ 和 add.__doc__ 仍然有效。
實(shí)戰(zhàn)案例:日志記錄裝飾器
假設(shè)我們有一個(gè)應(yīng)用程序,需要記錄每個(gè)函數(shù)的調(diào)用時(shí)間和返回值。我們可以使用裝飾器來實(shí)現(xiàn)這一點(diǎn)。
示例:日志記錄裝飾器
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
在這個(gè)例子中,log_function_call 裝飾器記錄了 compute_sum 函數(shù)的調(diào)用時(shí)間、參數(shù)和返回值。time.sleep(1) 模擬了一個(gè)延遲,以便我們可以看到時(shí)間記錄的效果。
總結(jié)
本文介紹了 Python 裝飾器的基礎(chǔ)知識(shí),包括裝飾器的基本概念、帶參數(shù)的裝飾器、帶參數(shù)的被裝飾函數(shù)以及如何使用 functools.wraps 保留元數(shù)據(jù)。通過四個(gè)簡(jiǎn)單的示例,我們逐步深入理解了裝飾器的工作原理和應(yīng)用場(chǎng)景。最后,我們通過一個(gè)實(shí)戰(zhàn)案例展示了如何使用裝飾器來記錄函數(shù)的調(diào)用信息。