Wzorce projektowe i polimorfizm w Pythonie

Mateusz Bogolubow

Mateusz Bogolubow

29-09-2026

(aktualizacja: 29-09-2026)

Polimorfizm i wzorce projektowe (Singleton, Factory, Strategy) to fundament, który ułatwia pisanie kodu w Pythonie łatwiejszego w rozbudowie i utrzymaniu — poznaj je na praktycznych przykładach kodu.

Wzorce projektowe i polimorfizm w Pythonie
Polimorfizm to jeden z czterech filarów programowania obiektowego — mechanizm pozwalający różnym typom obiektów reagować na to samo wywołanie metody na swój własny sposób, bez konieczności sprawdzania, z jakim dokładnie typem mamy do czynienia. W Pythonie polimorfizm działa dzięki tzw. duck typingu i jest fundamentem większości klasycznych wzorców projektowych, takich jak Singleton, Factory czy Strategy — wzorców, które pozwalają pisać kod łatwiejszy w rozbudowie i utrzymaniu, zamiast rozwiązywać ten sam problem architektoniczny od zera za każdym razem.

Znasz już składnię Pythona i potrafisz napisać działający program — ale kod zaczyna się robić coraz trudniejszy w rozbudowie, a każda zmiana wymaga poprawek w kilku miejscach naraz? To dokładnie ten moment, w którym warto poznać dwa powiązane ze sobą tematy: polimorfizm (jeden z fundamentów programowania obiektowego) oraz wzorce projektowe, które na tym fundamencie się opierają. To nie jest wiedza czysto akademicka — pytanie o implementację jednego z najpopularniejszych wzorców, Singletona, zebrało na Stack Overflow ponad 850 tysięcy wyświetleń, co pokazuje, jak często realnie programiści się z tym mierzą.

Czym jest polimorfizm w Pythonie?

Polimorfizm (z greckiego "wiele form") to mechanizm pozwalający różnym typom obiektów reagować na to samo wywołanie metody na swój własny, specyficzny dla siebie sposób. W praktyce oznacza to, że możesz napisać kod, który operuje na obiekcie w sposób ogólny, nie wiedząc (i nie musząc wiedzieć) z jakim dokładnie typem ma do czynienia w danym momencie.

class Pies:
    def wydaj_dzwiek(self):
        return "Hau!"

class Kot:
    def wydaj_dzwiek(self):
        return "Miau!"

def przedstaw_zwierze(zwierze):
    print(zwierze.wydaj_dzwiek())

przedstaw_zwierze(Pies())  # Hau!
przedstaw_zwierze(Kot())   # Miau!

Funkcja przedstaw_zwierze nie musi wiedzieć, czy dostała psa czy kota — wywołuje wydaj_dzwiek() i ufa, że każdy obiekt, który do niej trafi, będzie wiedział, jak na to zareagować. To właśnie polimorfizm w praktyce.

W Pythonie ten mechanizm działa dzięki tzw. duck typingu ("jeśli coś chodzi jak kaczka i kwacze jak kaczka, to prawdopodobnie jest kaczką") — w przeciwieństwie do języków takich jak Java czy C++, Python nie wymaga, żeby Pies i Kot dziedziczyły po wspólnej klasie bazowej, żeby polimorfizm zadziałał. Wystarczy, że obiekt ma metodę o odpowiedniej nazwie — Python nie sprawdza typu, tylko po prostu próbuje wywołać metodę. To jedna z rzeczy, która najbardziej zaskakuje osoby przechodzące do Pythona z języków statycznie typowanych, i dokładnie to pytanie („How does polymorphism work in Python?") zebrało na Stack Overflow blisko 50 tysięcy wyświetleń.

Jeśli mimo wszystko zależy Ci na wspólnym "kontrakcie" między klasami (np. żeby mieć pewność, że każda podklasa faktycznie implementuje daną metodę), możesz jawnie zdefiniować klasę bazową i dziedziczyć po niej:

class Zwierze:
    def wydaj_dzwiek(self):
        raise NotImplementedError("Podklasa musi zaimplementować tę metodę")

class Pies(Zwierze):
    def wydaj_dzwiek(self):
        return "Hau!"

class Kot(Zwierze):
    def wydaj_dzwiek(self):
        return "Miau!"

Oba podejścia są w Pythonie prawidłowe — duck typing jest bardziej "pythoniczny" i elastyczny, a dziedziczenie po wspólnej klasie bazowej daje więcej bezpieczeństwa i czytelności w większych projektach zespołowych.

Czym są wzorce projektowe i po co ich używać?

Wzorce projektowe to sprawdzone, wielokrotnie przetestowane rozwiązania powtarzających się problemów architektonicznych — nie konkretny kod do skopiowania, tylko sposób myślenia o strukturze programu. Zamiast wymyślać od zera, jak zorganizować kod odpowiadający za tworzenie obiektów, zarządzanie stanem czy komunikację między komponentami, sięgasz po rozwiązanie, które tysiące programistów przed Tobą już przetestowały i opisały.

Większość klasycznych wzorców projektowych (opisanych po raz pierwszy w słynnej książce "Gang of Four" z 1994 roku) opiera się właśnie na polimorfizmie jako mechanizmie — to dlatego oba tematy naturalnie się ze sobą łączą. Wzorce projektowe dokumentuje się zwykle za pomocą diagramów klas UML, pokazujących zależności (dziedziczenie, kompozycję) między klasami biorącymi udział we wzorcu — jeśli ta notacja jest Ci obca, zacznij od podstaw UML i diagramów klas, zanim przejdziesz dalej. Poniżej trzy wzorce, z którymi najczęściej mierzą się programiści Pythona w praktyce.

Wzorzec Singleton w Pythonie

Singleton gwarantuje, że z danej klasy w całym programie istnieje tylko jeden obiekt (instancja) — i że każde kolejne odwołanie do tej klasy zwraca dokładnie ten sam obiekt, a nie tworzy nowego. Używa się go najczęściej tam, gdzie dzielony zasób musi być jeden na cały program: połączenie z bazą danych, obiekt konfiguracji aplikacji czy logger.

class Konfiguracja:
    _instancja = None

    def __new__(cls):
        if cls._instancja is None:
            cls._instancja = super().__new__(cls)
            cls._instancja.ustawienia = {}
        return cls._instancja

config1 = Konfiguracja()
config2 = Konfiguracja()
print(config1 is config2)  # True — to ten sam obiekt

To właśnie pytanie o "najlepszy sposób" implementacji Singletona w Pythonie jest jednym z najpopularniejszych pytań o Pythona na Stack Overflow w historii tego serwisu (ponad 850 tysięcy wyświetleń) — głównie dlatego, że w Pythonie (w przeciwieństwie np. do Javy) nie ma jednego, "kanonicznego" sposobu na jego implementację. Powyższy przykład, wykorzystujący metodę __new__, to jedno z kilku poprawnych podejść — inne popularne warianty to moduł jako singleton (moduły w Pythonie są importowane tylko raz) albo dekorator klasy.

Wzorzec Factory (Fabryka) w Pythonie

Wzorzec Factory przenosi logikę tworzenia obiektów do osobnej funkcji lub klasy, zamiast rozrzucać ją po całym kodzie. Dzięki temu, gdy trzeba dodać nowy typ obiektu, zmieniasz kod w jednym miejscu — a nie wszędzie tam, gdzie dany obiekt jest tworzony.

class PowiadomienieEmail:
    def wyslij(self, tresc):
        print(f"E-mail: {tresc}")

class PowiadomienieSMS:
    def wyslij(self, tresc):
        print(f"SMS: {tresc}")

def utworz_powiadomienie(typ):
    if typ == "email":
        return PowiadomienieEmail()
    elif typ == "sms":
        return PowiadomienieSMS()
    raise ValueError(f"Nieznany typ powiadomienia: {typ}")

powiadomienie = utworz_powiadomienie("email")
powiadomienie.wyslij("Cześć!")

Zwróć uwagę, że utworz_powiadomienie zwraca różne typy obiektów, ale każdy z nich ma tę samą metodę wyslij — to znów polimorfizm w akcji, tym razem jako fundament, na którym opiera się cały wzorzec.

Wzorzec Strategy w Pythonie

Strategy pozwala wymieniać konkretny algorytm (sposób realizacji jakiegoś zadania) w trakcie działania programu, bez zmiany kodu, który z tego algorytmu korzysta. To jeden z wzorców najbardziej wprost opartych na polimorfizmie — cała jego idea polega na tym, że różne "strategie" mają ten sam interfejs, więc można je podmieniać jak klocki.

class StrategiaSortowania:
    def sortuj(self, dane):
        raise NotImplementedError

class SortowanieRosnace(StrategiaSortowania):
    def sortuj(self, dane):
        return sorted(dane)

class SortowanieMalejace(StrategiaSortowania):
    def sortuj(self, dane):
        return sorted(dane, reverse=True)

class Kontekst:
    def __init__(self, strategia: StrategiaSortowania):
        self.strategia = strategia

    def wykonaj(self, dane):
        return self.strategia.sortuj(dane)

kontekst = Kontekst(SortowanieRosnace())
print(kontekst.wykonaj([3, 1, 2]))  # [1, 2, 3]

kontekst.strategia = SortowanieMalejace()
print(kontekst.wykonaj([3, 1, 2]))  # [3, 2, 1]

W praktyce Strategy przydaje się wszędzie tam, gdzie masz kilka wariantów tego samego zadania — różne sposoby walidacji danych, różne metody płatności w sklepie internetowym, różne algorytmy rekomendacji — a chcesz móc je wymieniać bez ingerencji w resztę systemu.

Czy devmentor.pl uczy wzorców projektowych?

Uczciwie: ścieżka Python w devmentor.pl nie ma dziś osobnego, dedykowanego modułu poświęconego wzorcom projektowym — program skupia się na Django, REST API (Django REST Framework) oraz Dockerze i wdrożeniu. Dobra wiadomość jest taka, że samo Django intensywnie wykorzystuje programowanie obiektowe (klasy widoków, modele, formularze to w praktyce gotowe przykłady polimorfizmu i wzorców takich jak Template Method), więc ucząc się Django, naturalnie stykasz się z tymi konceptami w praktyce — ten artykuł to solidne, samodzielne wprowadzenie do teorii, która stoi za tym, co i tak zobaczysz w kodzie.

Podsumowanie

Polimorfizm i wzorce projektowe to nie wiedza "na później", tylko fundament, który ułatwia czytanie i pisanie kodu w praktycznie każdym większym projekcie — w tym w Django, którego uczysz się w ramach ścieżki backendowej. Jeśli chcesz pogłębić ten temat pod okiem mentora i przełożyć go na realny, komercyjny projekt — sprawdź program ścieżki Python albo od razu umów bezpłatną rozmowę.

Konsultacja i warsztaty devmentor.pl

Masz pytania?