Python 中的裝飾器

Python 中的裝飾器

本文介紹 Python 中的裝飾器。

YouTube Video

Python 中的裝飾器

Python 中的裝飾器是一個強大的功能,用於為函數或方法添加額外的功能。裝飾器允許在不修改現有程式碼的情況下添加新功能,從而提高程式碼的可重用性和可維護性。

裝飾器的基礎

Python 裝飾器的工作原理是將函數作為參數,並返回一個帶有新增功能的新函數。使用裝飾器,您可以輕鬆地為函數添加前處理或後處理功能。

如果沒有裝飾器,改變函數的行為需要直接修改函數本身。有了裝飾器,您可以在不修改原始實現的情況下擴展功能。

裝飾器的基本結構

 1def my_decorator(func):
 2    def wrapper():
 3        print("Before the function call")
 4        func()
 5        print("After the function call")
 6    return wrapper
 7
 8@my_decorator
 9def say_hello():
10    print("Hello!")
11
12say_hello()
  • 在這個例子中,my_decorator 函數作為裝飾器,用於包裝 say_hello 函數。@my_decorator 語法用於應用裝飾器,當呼叫 say_hello() 時,裝飾器所提供的前處理和後處理將自動執行。

裝飾器的運作原理

裝飾器按照以下步驟運作。

  1. 裝飾器以函數(或方法)作為參數。

  2. 它定義了一個包裝函數來執行原始函數。

  3. 包裝函數在執行原始函數之前或之後執行其他處理。

  4. 裝飾器返回包裝函數。因此,原始函數將被替換為具有裝飾功能的新函數。

適用於帶參數函數的裝飾器

當對帶有參數的函數應用裝飾器時,必須調整包裝函數以接受這些參數。

 1def my_decorator(func):
 2    def wrapper(*args, **kwargs):
 3        print("Before the function call")
 4        result = func(*args, **kwargs)
 5        print("After the function call")
 6        return result
 7    return wrapper
 8
 9@my_decorator
10def greet(name):
11    print(f"Hello, {name}!")
12
13greet("Alice")
  • 通過使用 *args**kwargs,可以處理接受任意數量參數和關鍵字參數的函數。這使得裝飾器可以普遍應用於具有任何類型參數的函數。

應用裝飾器的範例

用於日誌記錄的裝飾器

裝飾器通常用於添加日誌記錄功能。例如,可以創建一個裝飾器,在執行函數之前和之後進行記錄,這樣可以記錄函數被調用的時間以及執行所需的時間。

 1import time
 2
 3def log_time(func):
 4    def wrapper(*args, **kwargs):
 5        start_time = time.time()
 6        print(f"Calling function '{func.__name__}'...")
 7        result = func(*args, **kwargs)
 8        end_time = time.time()
 9        print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10        return result
11    return wrapper
12
13@log_time
14def long_task(duration):
15    time.sleep(duration)
16    print("Task completed!")
17
18long_task(2)
  • 在此範例中,log_time 裝飾器會測量函數的執行時間並將其作為日誌輸出。long_task 函數被該裝飾器包裝,其執行時間在運行期間被記錄。

用於權限管理的裝飾器

裝飾器也可以用於權限管理。例如,可以通過檢查用戶是否具有特定權限來限制處理。

 1def requires_permission(user_role):
 2    def decorator(func):
 3        def wrapper(*args, **kwargs):
 4            if user_role != "admin":
 5                print("Permission denied!")
 6                return
 7            return func(*args, **kwargs)
 8        return wrapper
 9    return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13    print(f"User {user_id} has been deleted.")
14
15delete_user(123)  # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456)  # Permission denied!
  • requires_permission 裝飾器根據用戶的角色限制函數的執行。通過將此類邏輯集中於裝飾器中,可以防止權限管理邏輯分散在代碼中,從而提高可讀性。

嵌套裝飾器

可以應用多個裝飾器。當對一個函數應用多個裝飾器時,它們會按照從上到下的順序執行。

 1def uppercase(func):
 2    def wrapper(*args, **kwargs):
 3        result = func(*args, **kwargs)
 4        return result.upper()
 5    return wrapper
 6
 7def exclaim(func):
 8    def wrapper(*args, **kwargs):
 9        result = func(*args, **kwargs)
10        return result + "!"
11    return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16    return f"Hello, {name}"
17
18print(greet("Alice"))  # "HELLO, ALICE!"
  • 在此範例中,對 greet 函數應用了兩個裝飾器。@exclaim 裝飾器在字符串末尾添加了一個驚嘆號,然後 @uppercase 將字符串轉換為大寫。

用於類方法的裝飾器

裝飾器也可以用於類方法。當需要控制類內方法的行為時,這特別有用。將裝飾器應用於類方法時,需注意例如 selfcls 等參數。

 1def log_method_call(func):
 2    def wrapper(self, *args, **kwargs):
 3        print(f"Calling method '{func.__name__}'...")
 4        return func(self, *args, **kwargs)
 5    return wrapper
 6
 7class MyClass:
 8    @log_method_call
 9    def greet(self, name):
10        print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")
  • 在此範例中,log_method_call 裝飾器被應用於 greet 方法,以在該方法被調用時輸出日誌。

使用裝飾器的注意事項

使用裝飾器時需要注意一些事項。裝飾器可能會更改原始函數的名稱和文檔字符串(例如,__name____doc__),因此建議使用 functools.wraps 來保留這些資訊。

 1import functools
 2
 3# Decorator without functools.wraps
 4def bad_decorator(func):
 5    def wrapper(*args, **kwargs):
 6        print("Before the function call")
 7        return func(*args, **kwargs)
 8    return wrapper
 9
10# Decorator with functools.wraps
11def good_decorator(func):
12    @functools.wraps(func)
13    def wrapper(*args, **kwargs):
14        print("Before the function call")
15        return func(*args, **kwargs)
16    return wrapper
17
18# Apply decorators
19@bad_decorator
20def my_function_bad():
21    """This is the original docstring for my_function_bad."""
22    print("Executing my_function_bad")
23
24@good_decorator
25def my_function_good():
26    """This is the original docstring for my_function_good."""
27    print("Executing my_function_good")
28
29# Run functions
30print("=== Without functools.wraps ===")
31my_function_bad()
32print("Function name:", my_function_bad.__name__)
33print("Docstring:", my_function_bad.__doc__)
34
35print("\n=== With functools.wraps ===")
36my_function_good()
37print("Function name:", my_function_good.__name__)
38print("Docstring:", my_function_good.__doc__)
  • 使用 @functools.wraps 確保原始函數的元數據正確傳遞給包裝函數。

總結

Python 裝飾器是一個非常強大的工具,可讓您簡潔地為函數和方法添加額外的功能。它們可以用於各種情境以減少程式碼重複並提高可維護性和可重用性。通過理解裝飾器的工作方式,您可以撰寫更高效且更靈活的程式碼。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video