XiWind 西風之劍
HomeTechProContactGitHub
  • About
  • Git
    • Windows Terminal、PowerShell 安裝
    • Git 開始使用
    • Branch 入門
    • 合併多個 Commit , 編輯
    • 額外功能
  • deep learning
    • Dilated Convolution
  • Python
    • GIL 【全域直譯器鎖】
    • PyPy 【JIT 編譯器】
    • Decorator 【修飾器】
      • Class Decorators
  • Python library
    • abc 【抽象 Class】
      • ABC, ABCMeta
      • __abstractmethods__, get_cache_token, update_abstractmethods
    • dataclasses 【數據 Class】
      • make_dataclass(), replace(), is_dataclass(), __post_init__
    • enum 【列舉 Class】
      • Flag, auto(), unique, verify()
      • 範例
    • concurrent.futures 【執行緒、程序】
      • Future, Module Functions
    • queue 【佇列、同步】
      • full(), empty(), qsize(), join(), task_done()
    • functools 【可調用物件】
      • ordering、wrapper、partial
      • Overloading
    • heapq 【堆積佇列】
      • heapify(), merge(), nlargest(), nsmallest()
    • time 【時間】
      • time(), monotonic(), perf_counter()...
      • sleep(), 範例...
    • logging 【日誌】
Powered by GitBook
On this page
  • 範例 – 基礎範例
  • 帶參數的 Decorators (Decorator factory)
  • Decorators 應用順序
  • Syntactic sugar

Was this helpful?

  1. Python

Decorator 【修飾器】

PreviousPyPy 【JIT 編譯器】NextClass Decorators

Last updated 1 year ago

Was this helpful?


Python 中的 decorator 是一個修改另一個函數功能的函數。 Decorator 可以被視為使用另一個函數擴展其行為,而不顯式修改它的函數。 由 @ 語法表示,可以在函數或 Class 定義之前使用。

Decorators 實現了以下幾個目標:

  1. 程式碼可重用性和 Don't Repeat Yourself 原則: Decorators 允許您定義可重用的構建 blocks,這些構建 blocks 可以更改或擴展其他函數或方法的行為。 它們可以應用於任何需要附加功能的函數或方法,從而提高程式碼的可重用性並遵守 原則。

  2. 促進 Separation of concerns: 透過在 decorator 中封裝對函數行為的更改或新加,您可以使主函數的程式碼保持簡潔並更加專注於其主要任務。 這支援了 (關注點分離) 的設計原則。

  3. 在不修改函數程式碼的情況下更改行為: 使用 decorators,您可以在不更改函數 source code 的情況下更改函數的行為。 當您想要向無法或不想更改的函數添加行為時,這會很有用。 例如:向 Python 標準 library 或第三方 library 中的函數,增加日誌記錄、計時功能、記憶性。

Decorators 可以顯著改變它們所裝飾的函數的行為,因此使用它們時要小心。

誤用可能會導致程式碼難以理解、閱讀和 debug。因此應謹慎使用它們並徹底記錄。

範例 – 基礎範例


my_decorator 是一個接受另一個函數作為參數的函數 (在本例中為 say_hello)。 my_decorator 中的 wrapper 函數是一個嵌套 (nested) 函數,它調用 say_hello (原始函數),但還在函數調用之前和之後加入了一些功能。

PYTHON
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()

# =========================================================
# Replace the decorator

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

def say_hello():
    print("Hello!")

decorated_say_hello = my_decorator(say_hello)
decorated_say_hello()

執行結果:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

帶參數的 Decorators (Decorator factory)


Decorators 也可以接受參數 (arguments)。 以下是定義帶有參數的 decorator 的方法:

scale_and_shift 是一個 decorator factory:返回 decorator 的函數。 當您使用 @scale_and_shift(...) 語法時,您首先調用 scale_and_shift(...),它返回實際的decorator。

Decorator 是一個函數,它接受一個函數 func 作為參數,並返回一個 wraps (包裝) func 的新函數 wrapper。 wrapper 函數使用它收到的任何參數調用 func。

Decorator 在 wrapper 函數中使用 *args 和 **kwargs,允許 func 接受任意數量和類型的參數。

所有 decorators 只是一種接受一個參數並返回一個值,除了使用 decorator factory。

PYTHON
def scale_and_shift(factor, offset=0):
    def decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs) * factor + offset
        return wrapper
    return decorator

# Use the decorator with both a factor and an offset.
@scale_and_shift(2, offset=3)
def foo(x):
    return x
print(foo(5))  # Output: (5*2) + 3 = 13

# Use the decorator with only a factor. The offset defaults to 0.
@scale_and_shift(3)
def bar(x):
    return x
print(bar(5))  # Output: (5*3) + 0 = 15

# =========================================================
# 【Replace the decorator】
def scale_and_shift(factor, offset=0):
    def decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs) * factor + offset
        return wrapper
    return decorator

def foo(x):
    return x

# Create a new function by applying the decorator manually
foo_decorated = scale_and_shift(2, offset=3)(foo)
print(foo_decorated(5))  # Output: (5*2) + 3 = 13

def bar(x):
    return x

# Create a new function by applying the decorator manually
bar_decorated = scale_and_shift(3)(bar)
print(bar_decorated(5))  # Output: (5*3) + 0 = 15

Decorators 應用順序


Python 中的裝飾器按照它們的編寫順序應用,即從最接近函數的位置開始向上。 為了更好地理解這一點,請考慮以下程式碼:

函數 greet() 的輸出被傳遞給 decorator2,然後 decorator2(greet) 的輸出被傳遞給 decorator1。

PYTHON
def decorator1(func):
    def wrapper():
        print("Entering decorator1")
        func()
        print("Exiting decorator1")
    return wrapper

def decorator2(func):
    def wrapper():
        print("Entering decorator2")
        func()
        print("Exiting decorator2")
    return wrapper

@decorator1
@decorator2
def greet():
    print("Hello!")

greet()

執行結果:

Entering decorator1
Entering decorator2
Hello!
Exiting decorator2
Exiting decorator1

Python 在函數定義時運行 decorators,而不是在函數調用時運行。 這使得裝飾器適合在定義函數時需要完成一次的任務,例如: 將函數註冊為事件的處理程序。

PYTHON
def my_decorator(func):
    print("Decorating function.")
    return func

@my_decorator
def my_function():
    print("Executing function.")

# Output: Decorating function.

Syntactic sugar


Syntactic sugar (語法糖) 是一個在編程中使用的術語,用於描述編程語言中的語法,目的在使內容更易於閱讀或表達。 它的目的是讓語言更適合人類使用,也就是說,它使編碼更簡單或更易讀。

與許多語言一樣,Python 也有大量的 syntactic sugar。 這不會改變程式碼的作用; 它只是以不同的方式編寫相同的內容。例如:

PYTHON
numbers = [1, 2, 3, 4, 5]
squared = []
for number in numbers:
    squared.append(number ** 2)
print(squared)  # Output: [1, 4, 9, 16, 25]

numbers = [1, 2, 3, 4, 5]
squared = [number ** 2 for number in numbers]
print(squared)  # Output: [1, 4, 9, 16, 25]

Python 中的 decorators 也是一種 syntactic sugar 。函數前面的 @ 符號是 decorator。 它是 syntactic sugar,消除了將 decorator 作為函數本身調用的需要,並使程式碼更清晰。

雖然 syntactic sugar 可以使程式碼更易於閱讀和編寫,但有時也會使其變得更加複雜,特別是對於那些不熟悉所使用的習慣用法和語法的人來說。 因此,需要明智地使用這些工具,並確保可讀性和表達能力。

PYTHON
# 【decorator】
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()

# =========================================================
# 【Replace the decorator】
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

def say_hello():
    print("Hello!")

say_hello = my_decorator(say_hello)
say_hello()

執行結果:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

更多範例: , ,

,

DRY (不要重複自己)
Separation of concerns
@wraps()
partial()
測量函數的性能
速率限制器
Page cover image
Logger Decorator