Python 中的裝飾器如何工作?
裝飾器(Decorator)是 Python 中一種高級特性,它允許程序員修改或增強函數和方法的行為,而無需直接更改其源代碼。裝飾器本質上是一個返回函數的高階函數,它可以接收一個函數作為參數,并返回一個新的或修改后的函數。通過使用裝飾器,您可以實現諸如日志記錄、訪問控制、性能測量等功能,同時保持代碼的清晰和模塊化。
裝飾器的工作原理
裝飾器定義:首先,您需要定義一個裝飾器函數。這個函數接受一個被裝飾的函數作為參數,并返回一個新的函數(通常是內部定義的一個閉包),該新函數可以在執行原始函數之前或之后添加額外邏輯。
應用裝飾器:要將裝飾器應用于某個函數,可以使用 @decorator_name 語法糖,放在函數定義之前。這相當于在函數定義后立即調用裝飾器并將函數作為參數傳遞給它。
執行流程:當調用被裝飾的函數時,實際上是在調用裝飾器返回的新函數。這意味著您可以在實際執行目標函數之前或之后插入自定義行為。
示例:基本裝飾器
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 函數,在調用前后打印消息。
帶參數的裝飾器
有時,您可能希望裝飾器本身也能接受參數。為了實現這一點,可以創建一個“裝飾器工廠”——即一個返回裝飾器的函數。
示例:帶參數的裝飾器
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello {name}")
greet("Alice")
# 輸出:
# Hello Alice
# Hello Alice
# Hello Alice
這里,repeat 是一個裝飾器工廠,它根據提供的 num_times 參數生成一個具體的裝飾器。
類裝飾器
除了函數,Python 還支持類裝飾器。類裝飾器通常用于修改類的行為或屬性。它們接收類作為參數,并返回一個新的類或修改后的類。
示例:類裝飾器
def add_method(cls):
def decorator(func):
setattr(cls, func.__name__, func)
return cls
return decorator
@add_method
class MyClass:
pass
@add_method(MyClass)
def new_method(self):
print("This is a dynamically added method.")
obj = MyClass()
obj.new_method() # 輸出: This is a dynamically added method.
在這個例子中,add_method 是一個類裝飾器,它向 MyClass 動態添加了一個新的方法。
內置裝飾器
Python 提供了一些內置的裝飾器來簡化常見的編程任務:
@classmethod 和 @staticmethod:用于定義類方法和靜態方法。
@property:用于將類的方法轉換為只讀屬性。
@functools.lru_cache:用于緩存函數的結果以提高性能。
@dataclasses.dataclass:用于自動為類生成特殊方法(如 __init__() 和 __repr__())。
示例:使用 @property
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""The radius property."""
print("Getting radius")
return self._radius
@radius.setter
def radius(self, value):
if value >= 0:
self._radius = value
else:
raise ValueError("Radius must be non-negative")
circle = Circle(5)
print(circle.radius) # 輸出: Getting radius\n5
circle.radius = 10
print(circle.radius) # 輸出: Getting radius\n10
使用多個裝飾器
如果一個函數被多個裝飾器修飾,則這些裝飾器按照從下到上的順序依次應用。也就是說,最接近函數定義的裝飾器最先執行。
示例:多個裝飾器
def decorator_one(func):
def wrapper():
print("Decorator one")
func()
return wrapper
def decorator_two(func):
def wrapper():
print("Decorator two")
func()
return wrapper
@decorator_one
@decorator_two
def hello():
print("Hello!")
hello()
# 輸出:
# Decorator one
# Decorator two
# Hello!
在這個例子中,decorator_two 首先被應用,然后是 decorator_one。
總結
裝飾器是 Python 中非常強大且靈活的工具,它們可以讓您以優雅的方式擴展或修改函數和類的功能。通過理解裝飾器的工作原理以及如何構建自己的裝飾器,您可以編寫出更加簡潔、可維護和功能豐富的代碼。隨著經驗的積累,您還將發現更多關于裝飾器的高級用法和最佳實踐。