Side-by-Side Language Deep Dive

Dekoratory Pythona a wzorzec projektowy dekoratora: kluczowe różnice

Porównaj dekoratory Pythona z klasycznym wzorcem projektowym dekoratora. Zrozum różnice między zawijaniem funkcji w czasie definicji a dynamiczną kompozycją obiektów w czasie wykonywania z możliwym do uruchomienia kodem.

Chociaż dekoratory Pythona i wzorzec projektowy dekoratorów mają tę samą nazwę i ogólną koncepcję „zawijania” kodu w celu rozszerzenia zachowania, reprezentują zupełnie inne konstrukcje i filozofie programowania.

Dekorator Pythona to funkcja języka — cukier syntaktyczny, który pozwala opakować funkcję, metodę lub klasę w momencie definicji. Jest to forma metaprogramowania, która jest wykonywana podczas importowania lub definiowania modułu, zmieniając zachowanie tej funkcji przez resztę działania aplikacji.

Wzorzec projektowy Dekoratora, pierwotnie zdefiniowany przez Gang Czterech (GoF), jest zorientowanym obiektowo wzorcem projektowania strukturalnego. Został zaprojektowany tak, aby dynamicznie dodawać obowiązki do poszczególnych obiektów w czasie wykonywania, bez wpływu na inne obiekty tej samej klasy, poprzez użycie klas opakowań i dynamicznej kompozycji.

Matryca porównania funkcji

FeaturePythonOther Language
Podstawowa koncepcjaCukier syntaktyczny na poziomie języka do zawijania funkcji/klas.Wzorzec projektowy do dynamicznego dodawania zachowań do poszczególnych obiektów.
Czas wykonaniaCzas definicji (uruchamiany raz po załadowaniu modułu).Czas wykonania (tworzony dynamicznie podczas wykonywania programu).
Jednostka docelowaFunkcje, metody lub całe klasy (zakres statyczny).Konkretne instancje obiektów (zakres dynamiczny).
Metoda wdrożeniaZamknięcia i funkcje pomocnicze wykorzystujące składnię `@dekorator`.Dziedziczenie klas, wyrównanie interfejsu i kompozycja obiektów.

Syntax & Idiom Comparisons

01.Dekorator Pythona: opakowanie funkcji (czas definicji)

Dekoratorzy Pythona wykorzystują pierwszorzędne funkcje do przechwytywania połączeń. Kiedy napiszesz `@my_decorator`, Python przekazuje dekorowaną funkcję do twojej funkcji dekoratora i wiąże zwróconą funkcję opakowania z oryginalną nazwą funkcji.

W tym przykładzie dekorator `@log_call` otacza prostą funkcję śledzącą moment jej wykonania i to, co zwraca. Należy pamiętać, że to opakowanie jest niezmienne w momencie zdefiniowania funkcji.

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.Wzorzec projektowy dekoratora: opakowanie obiektu (kompozycja w czasie wykonywania)

Klasyczny wzorzec projektowy Dekoratora wykorzystuje kompozycję klas. Masz klasę komponentu bazowego i klasy dekoratorów, które otaczają ten komponent bazowy. W czasie wykonywania łączysz te konstruktory, aby dynamicznie budować dostosowany obiekt.

Tutaj wdrażamy klasyczny system dekoratora kawy. Możemy zawinąć podstawowy obiekt Coffee w obiekt MilkDecorator i zawinąć go w obiekt SugarDecorator, dynamicznie obliczając ostateczny koszt w czasie wykonywania.

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.

Werdykt i podsumowanie

Używaj dekoratorów Pythona (@decorator), jeśli chcesz zastosować przekrojowe zagadnienia (takie jak rejestrowanie, synchronizacja, uwierzytelnianie, buforowanie lub sprawdzanie poprawności danych wejściowych) do jednolitych funkcji lub tras w aplikacji.

Wzorca projektowego Dekoratora należy używać, gdy trzeba dynamicznie dołączać lub odłączać zachowania od poszczególnych instancji obiektów w czasie wykonywania, zwłaszcza podczas łączenia wielu opcjonalnych klas opakowań w dowolną sekwencję.

Często zadawane pytania

Dlaczego strona Wikipedii mówi, że dekoratory Pythona nie są wzorcem dekoratora?

Ponieważ standardowe dekoratory Pythona otaczają funkcje i definicje klas w czasie kompilacji/definicji, wpływając na wszystkie instancje tej funkcji. Klasyczny wzorzec projektowy dynamicznie otacza poszczególne instancje obiektów w czasie wykonywania.

Czy wzorzec dekoratora GoF jest przydatny w Pythonie?

Tak, ale metoda kaczego pisania w Pythonie sprawia, że jest ona znacznie prostsza w implementacji niż w językach z typem statycznym, takich jak Java czy C++, ponieważ dekoratory nie muszą dziedziczyć z formalnego interfejsu podstawowego.

Inne porównania

Polecane zasoby Pythona

Poszerzaj swoją wiedzę dzięki powiązanym interaktywnym samouczkom, ściągawkom i porównaniom kodów.