Side-by-Side Language Deep Dive

Python 裝飾器與裝飾器設計模式:主要區別

比較 Python 裝飾器和經典的裝飾器設計模式。了解定義時函數包裝和使用可運行程式碼的執行時間動態物件組合之間的差異。

雖然 Python 裝飾器和裝飾器設計模式具有相同的名稱和「包裝」程式碼以擴展行為的一般概念,但它們代表了完全不同的程式結構和哲學。

Python 裝飾器是一種語言功能 - 語法糖,可讓您在定義時包裝函數、方法或類別。它是一種元編程形式,在導入或定義模組時執行,改變該函數在應用程式運行的其餘部分的行為方式。

裝飾器設計模式最初由四人幫(GoF)定義,是一種物件導向的結構設計模式。它旨在透過使用包裝類別和動態組合,在運行時動態地向各個物件添加職責,而不影響同一類別的其他物件。

特性比較矩陣

FeaturePythonOther Language
核心理念用於包裝函數/類別的語言級語法糖。用於向單一物件動態添加行為的設計模式。
執行時間定義時間(載入模組時運行一次)。運行時(在程式執行期間動態實例化)。
目標實體函數、方法或整個類別(靜態範圍)。特定物件實例(動態範圍)。
實施方法使用“@decorator”語法的閉包和輔助函數。類別繼承、介面對齊和物件組合。

Syntax & Idiom Comparisons

01.Python 裝飾器:函數包裝器(定義時間)

Python 裝飾器利用一流函數來攔截呼叫。當您編寫「@my_decorator」時,Python 將裝飾函數傳遞給您的裝飾函數,並將傳回的包裝函數綁定到原始函數名稱。

在此範例中,「@log_call」裝飾器包裝了一個簡單的函數來追蹤它的執行時間和返回內容。請注意,這種包裝在定義函數時就已確定。

def log_call(func):
    def wrapper(*args, **kwargs):
        print(f"[LOG] Executing '{func.__name__}' with args {args}")
        result = func(*args, **kwargs)
        print(f"[LOG] '{func.__name__}' returned: {result}")
        return result
    return wrapper

@log_call
def add(a, b):
    return a + b

# The wrapping occurs once at definition time
print("Result of add(5, 7):", add(5, 7))
python
// Decorators in Python are syntactic sugar for function wrappers:
// add = log_call(add)
// They exist primarily to avoid duplicate boilerplate for logging, timing, and auth.

02.裝飾器設計模式:物件包裝(運行時組合)

經典的裝飾設計模式使用類別組合。您有一個基本組件類別和包裝此基本組件的裝飾器類別。在運行時,您將這些建構函式連結在一起以動態建立自訂物件。

在這裡,我們實現了一個經典的咖啡裝飾器系統。我們可以將基本的 Coffee 物件包裝在 MilkDecorator 中,並將其包裝在 SugarDecorator 中,在運行時動態計算最終成本。

class Coffee:
    def get_cost(self):
        return 5.0
    def get_description(self):
        return "Simple Coffee"

class MilkDecorator:
    def __init__(self, coffee):
        self.coffee = coffee
    def get_cost(self):
        return self.coffee.get_cost() + 1.5
    def get_description(self):
        return self.coffee.get_description() + ", Milk"

class SugarDecorator:
    def __init__(self, coffee):
        self.coffee = coffee
    def get_cost(self):
        return self.coffee.get_cost() + 0.5
    def get_description(self):
        return self.coffee.get_description() + ", Sugar"

# Chaining wrappers dynamically at runtime
my_drink = Coffee()
my_drink = MilkDecorator(my_drink)
my_drink = SugarDecorator(my_drink)

print("Drink description:", my_drink.get_description())
print("Total Cost: $", my_drink.get_cost())
python
// Standard OO languages (like Java) require shared interfaces:
// interface Coffee { double getCost(); }
// class SimpleCoffee implements Coffee { ... }
// class MilkDecorator implements Coffee { ... }
// Python's duck typing allows dynamic composition without interfaces.

判決與總結

當您想要將橫切關注點(例如日誌記錄、計時、驗證、快取或輸入驗證)套用到整個應用程式中的函數或路由時,請使用 Python 裝飾器 (@decorator)。

當您需要在執行時間動態地附加或分離單一物件實例的行為時,尤其是在以任意序列組合多個可選包裝類別時,請使用裝飾器設計模式。

常見問題解答

為什麼維基百科頁面說 Python 裝飾器不是裝飾器模式?

因為標準 Python 裝飾器在編譯/定義時包裝函數和類別定義,從而影響該函數的所有實例。經典設計模式在運行時動態包裝各個物件實例。

GoF 裝飾器模式在 Python 中有用嗎?

是的,但是 Python 的鴨子類型使其實作比 Java 或 C++ 等靜態類型語言簡單得多,因為裝飾器不需要從正式的基底介面繼承。

其他比較

推薦的 Python 資源

透過相關的互動式教學、備忘單和程式碼比較來擴展您的知識。