Streszczenie Klasy danych (dataclass) w Pythonie

Ten moduł wprowadza dekorator @dataclass z modułu dataclasses, który automatycznie generuje metody __init__, __repr__ i __eq__, eliminując kod boilerplate (ponad 30 linii kodu zastępionych 5). Moduł ten, wprowadzony w Python 3.7 na mocy PEP 557, stanowi część standardowej biblioteki — nie wymaga instalacji. W przeciwieństwie do namedtuple, który również generuje klasy danych ale w formacie krotki, dataclass daje pełną elastyczność klas mutowalnych z możliwością dziedziczenia i własnych metod.

Omówione są adnotacje typów pól (str, int, float, list, Optional), wartości domyřlne z ostrzeżeniem przed pułapką mutowalnych argumentów domyřlnych, funkcja field() do zaawansowanej konfiguracji oraz parametry dekoratora: frozen=True dla klas niemutowalnych, order=True dla automatycznych operatorów porównania, init, repr i eq do selektywnego generowania. Przedstawione są również funkcje asdict(), astuple() do konwersji, metoda __post_init__() do walidacji oraz dziedziczenie z dataclass. Praktyczne przykłady klas Punkt, Osoba i Student ilustrują zastosowania. Każdy przykład pokazuje różny stopień zaawansowania — od prostych struktur po złożone domeny biznesowe.

  • Problem boilerplate'u — ręczne pisanie __init__, __repr__, __eq__ vs jedna linia z @dataclass. Typowa klasa danych wymaga ~30 linii kodu; @dataclass redukuje to do 5.
  • Dekorator @dataclass — parametry init, repr, eq, order, frozen, porównanie 30 vs 5 linii kodu. Dekorator analizuje adnotacje typów i generuje odpowiednie metody w locie.
  • Pola i typowanie — adnotacje typów, wartości domyřlne, pułapka mutowalnych domyślnych (None + field(default_factory=list)). Typy nie są wymuszane w runtime, ale służą narzędziom zewnętrznym.
  • Zaawansowane funkcjefield() z parametrami default, default_factory, init, repr, compare, hash; frozen=True dla niemutowalności (obiekty hashowalne), order=True dla sortowania.
  • Metody pomocnicze__post_init__ do walidacji stanu po utworzeniu, asdict() i astuple() do konwersji na słownik/krotkę, dziedziczenie dataclass z zachowaniem wszystkich wygenerowanych metod.
  • Porównanie z alternatywaminamedtuple daje niemutowalne krotki z nazwanymi polami, ale brak dziedziczenia i mutowalności. attrs zewnętrzna biblioteka z większą liczbą funkcji. dataclass to złoty środek wbudowany w standardową bibliotekę.
Streszczenie — Klasy danych

Moduł dotyczący pyMID OOP — Część 10: Klasy danych (dataclass) stanowi integralną część kompleksowego kursu programowania obiektowego w języku Python. Jego celem jest zarówno przedstawienie teoretycznych podstaw omawianego zagadnienia, jak i zaprezentowanie praktycznych przykładów jego zastosowania w codziennej pracy programisty. Materiał został zaprojektowany z myślą o studentach kierunków informatycznych, którzy chcą zrozumieć nie tylko składnię, ale przede wszystkim kontekst i uzasadnienie stosowania poszczególnych rozwiązań. Każdy slajd zawiera przykłady kodu, które można samodzielnie przetestować w środowisku REPL. Systematyczna praca z materiałem gwarantuje solidne opanowanie prezentowanych koncepcji. W kolejnych modułach wiedza ta będzie rozwijana o bardziej zaawansowane zagadnienia.

Zrozumienie omawianego tematu jest kluczowe dla dalszego rozwoju umiejętności programistycznych, ponieważ stanowi on fundament dla bardziej zaawansowanych koncepcji. Współczesne aplikacje webowe, systemy bazodanowe i frameworki w dużym stopniu opierają się na przedstawionych tu mechanizmach. Opanowanie tego materiału otwiera drzwi do zrozumienia zaawansowanych wzorców projektowych i architektury oprogramowania. Zachęcamy do cierpliwej i systematycznej nauki - każde nowe pojęcie będzie szczegółowo wyjaśnione i zilustrowane przykładami. Aktywne uczestnictwo poprzez samodzielne eksperymenty z kodem jest kluczem do sukcesu w nauce programowania.

1 Część 10: klasy danych (dataclass)

Pythonowe klasy danych

W tej części poznamy moduł dataclasses — sposób na szybkie i wygodne tworzenie klas przechowujących dane. Zobaczymy, jak zredukować ilość boilerplate'u, automatycznie generując metody __init__, __repr__, __eq__ i inne. Klasy danych to jeden z najczęstszych wzorców w Pythonie — większość klas to właśnie kontenery na dane, a dataclass automatyzuje ich tworzenie.

Moduł dataclasses został wprowadzony w Python 3.7 (PEP 557) i od razu zyskał ogromną popularność w społeczności. Łączy w sobie prostotę namedtuple z pełną mocą klas Pythona — możemy dodawać własne metody, dziedziczyć i kontrolować zachowanie każdego pola z osobna.

Omówimy również zaawansowane funkcje, takie jak pola niemutowalne (frozen=True), automatyczne porównywanie (order=True), domyślne wartości oraz funkcję field() do precyzyjnej konfiguracji. Na konkretnych przykładach zobaczymy, jak każda z tych opcji wpływa na zachowanie klasy w praktyce.

Wprowadzenie do dataclass

Temat Część 10: klasy danych (dataclass) stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

2 Cele dydaktyczne

Co dziś osiągniemy

Cel główny: Nauczysz się tworzyć klasy danych w Pythonie za pomocą dekoratora @dataclass i rozumieć, kiedy ich używać.
  • Rozumieć problem ręcznego pisania __init__, __repr__, __eq__ i dlaczego jest to źródło będów
  • Importować i stosować dekorator @dataclass z różnymi parametrami konfiguracyjnymi
  • Definiować pola z typowaniem (str, int, float, list, Optional) i rozumieć rolę adnotacji typów
  • Ustawiać domyślne wartości pól i unikać pułapek z mutowalnymi domyślnymi za pomocą field(default_factory=...)
  • Korzystać z field() do zaawansowanej konfiguracji każdego pola z osobna
  • Porównywać i sortować obiekty dataclass za pomocą domyślnego __eq__ i opcji order=True
  • Tworzyć niemutowalne klasy z frozen=True i używać ich jako kluczy w słownikach
  • Stosować order=True do automatycznego sortowania bez ręcznego pisania metod porównawczych
  • Poznać funkcje pomocnicze: asdict(), astuple(), replace() oraz metodę __post_init__() do walidacji
  • Rozumieć różnice między dataclass a namedtuple i wiedzieć, kiedy wybrać które rozwiązanie
Cele dydaktyczne

Cele dydaktyczne wyznaczają kierunek nauki i pomagają w systematycznym przyswajaniu wiedzy. Każdy cel koncentruje się na konkretnym aspekcie omawianego zagadnienia, od teorii przez praktykę aż po zaawansowane zastosowania. Realizacja wszystkich założeń gwarantuje solidne podstawy do dalszego rozwoju w kierunku bardziej złożonych zagadnień. Materiał został dostosowany do potrzeb studentów kierunków informatycznych, uwzględniając różne poziomy zaawansowania. Regularne sprawdzanie postępów pomoże w identyfikacji obszarów wymagających dodatkowej uwagi i powtórki. Struktura kursu przewiduje płynne przejście od zagadnień podstawowych do bardziej złożonych.

Oprócz celów czysto technicznych, kładziemy również nacisk na rozwój umiejętności analitycznego myślenia i rozwiązywania problemów. Umiejętność samodzielnego doboru odpowiednich narzędzi i technik jest równie ważna jak znajomość składni języka. Zachęcamy do aktywnego uczestnictwa w procesie nauki poprzez zadawanie pytań i poszukiwanie dodatkowych informacji. Wykorzystaj dostępne zasoby, takie jak dokumentacja Pythona, społecznościowe fora programistyczne i tutoriale wideo. Systematyczna praca i regularne powtórki są kluczem do sukcesu w opanowaniu programowania obiektowego w języku Python.

3 Problem — ręczne pisanie __init__, __repr__, __eq__

Ile kodu trzeba napisać?

Wyobraźmy sobie prostą klasę Osoba z polami imie, nazwisko i wiek. Bez użycia dataclass musimy ręcznie zdefiniować:

  • __init__ — konstruktor inicjalizujący wszystkie pola
  • __repr__ — reprezentację tekstową dla programisty (debugowanie)
  • __eq__ — porównywanie obiektów (bez tego == porównuje identyczność w pamięci)
  • Ewentualnie __lt__, __le__, __gt__, __ge__ do sortowania
  • __hash__ — jeśli obiekty mają być kluczami w słownikach

To dużo powtarzalnego kodu, który łatwo popełnić i trudno utrzymać. Każda zmiana w strukturze pól (dodanie/usunięcie) wymaga aktualizacji wszystkich tych metod. Często zdarza się, że programista dodaje nowe pole do __init__ ale zapomina zaktualizować __repr__ lub __eq__, co prowadzi do trudnych do wykrycia będów. Na kolejnych slajdach zobaczymy różnicę między ręcznym a automatycznym podejściem.

Problem boilerplate

Temat Problem — ręczne pisanie __init__, __repr__, __eq__ stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

4 Kod: klasa bez dataclass
class Osoba:
    def __init__(self, imie, nazwisko, wiek):
        self.imie = imie
        self.nazwisko = nazwisko
        self.wiek = wiek

    def __repr__(self):
        return f"Osoba(imie={self.imie!r}, nazwisko={self.nazwisko!r}, wiek={self.wiek!r})"

    def __str__(self):
        return f"{self.imie} {self.nazwisko} ({self.wiek} lat)"

    def __eq__(self, other):
        if not isinstance(other, Osoba):
            return NotImplemented
        return (self.imie, self.nazwisko, self.wiek) == (other.imie, other.nazwisko, other.wiek)

    def __hash__(self):
        return hash((self.imie, self.nazwisko, self.wiek))

    def __lt__(self, other):
        if not isinstance(other, Osoba):
            return NotImplemented
        return (self.nazwisko, self.imie) < (other.nazwisko, other.imie)

# Ponad 30 linii dla prostej klasy z zaledwie 3 polami!
# A to jeszcze nie uwzględnia __le__, __gt__, __ge__!
Klasa bez dataclass

Temat Kod: klasa bez dataclass stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

5 Kod: ta sama klasa z dataclass
from dataclasses import dataclass

@dataclass
class Osoba:
    imie: str
    nazwisko: str
    wiek: int

To wszystko! Pięć linii zamiast trzydziestu. Dekorator @dataclass automatycznie generuje:

  • __init__ — konstruktor przyjmujący argumenty w kolejności pól
  • __repr__ — reprezentację w formacie Osoba(imie='...', nazwisko='...', wiek=...)
  • __eq__ — porównanie strukturalne (wszystkie pola muszą być równe)

Możemy też własnoręcznie dodać dodatkowe metody, które uzupełnią te wygenerowane. Na przykład własne __str__ do ładnego wyświetlania lub metody biznesowe. Wszystkie ręcznie napisane metody mają pierwszeństwo przed wygenerowanymi.

Klasa z dataclass

Temat Kod: ta sama klasa z dataclass stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

6 Porównanie: 30 linii vs 5 linii

Różnica jest ogromna

Bez dataclass: ~30 linii kodu, ręczne implementacje, łatwo o błąd. Każda zmiana struktury pól wymaga aktualizacji wielu metod. Ryzyko rozjazdu między stanem faktycznym a implementacją metod.

Z dataclass: 5 linii, wszystko działa "od ręki", mniej błędów. Zmiana struktury = zmiana definicji pól. Reszta dzieje się automatycznie.

dataclass generuje kod dokładnie taki, jaki napisalibyśmy ręcznie — ale robi to za nas. Co więcej, jeśli zmienimy definicjĉ pól (dodamy nowe, usuniemy istniejące, zmienimy typy), wygenerowane metody automatycznie się dostosują. To ogromna oszczędność czasu i redukcja ryzyka. W projektach z wieloma klasami danych (często kilkadziesiąt lub więcej) oszczędność sięgnąć może tysięcy linii kodu, którego nie trzeba pisać ani utrzymywać.

Dodatkową zaletą jest spójność — wszystkie wygenerowane metody zachowują się przewidywalnie i zgodnie z oczekiwaniami, ponieważ pochodzą z jednego, przetestowanego źródła (standardowa biblioteka Pythona).

Porównanie

Temat Porównanie: 30 linii vs 5 linii stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

7 Wprowadzenie do @dataclass

Import z dataclasses

Moduł dataclasses jest wbudowany w Python 3.7+ (PEP 557). Nie wymaga instalacji — jest częścią standardowej biblioteki, więc działa na każdej instalacji Pythona 3.7 lub nowszej. W Python 3.6 można użyć dataclasses jako pakietu zewnętrznego (pip install dataclasses).

from dataclasses import dataclass

Dostępne są też inne narzędzia:

from dataclasses import dataclass, field, asdict, astuple, fields, replace
  • field() — zaawansowana konfiguracja pojedynczego pola
  • asdict() — konwersja obiektu na słownik
  • astuple() — konwersja obiektu na krotkę
  • fields() — introspekcja pól dataclass
  • replace() — tworzenie kopii z wybranymi zmianami (przydatne przy frozen=True)

Sam dekorator @dataclass przyjmuje parametry takie jak frozen, order, init, repr, eq i inne. Domyślnie wszystkie opcje są włączone z wyjątkiem frozen i order.

Import dataclass

Wprowadzenie do nowego tematu zawsze rozpoczynamy od przedstawienia kontekstu i uzasadnienia, dlaczego dane zagadnienie jest istotne w praktyce programistycznej. Każdy moduł kursu został zaprojektowany tak, aby stopniowo budować zrozumienie - od prostych analogii po konkretne implementacje w kodzie. Przedstawione koncepcje są fundamentem, na którym opierają się bardziej zaawansowane zagadnienia w kolejnych częściach kursu. Zaleca się aktywne zapoznanie z materiałem poprzez samodzielne testowanie przykładów i modyfikowanie ich w celu lepszego zrozumienia działania. Systematyczna praca i regularne powtórki są kluczem do sukcesu w nauce. Zachęcamy do korzystania z dodatkowych źródeł, takich jak oficjalna dokumentacja Pythona.

W tej części kursu skupimy się zarówno na teorii, jak i na praktycznych przykładach, które pozwolą na pełne zrozumienie omawianego zagadnienia. Każdy slajd zawiera elementy interaktywne, które zachęcają do samodzielnego eksperymentowania z kodem. Warto poświęcić czas na dokładne przeanalizowanie każdego przykładu i zrozumienie, dlaczego kod działa w określony sposób. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każdy komunikat błędu to cenna lekcja. Ćwiczenia praktyczne pomogą utrwalić zdobytą wiedzę i przygotować się do bardziej złożonych zadań.

8 Kod: pierwsza dataclass
from dataclasses import dataclass

@dataclass
class Punkt:
    x: float
    y: float

# Użycie
p = Punkt(3.5, 2.0)
print(p)       # Punkt(x=3.5, y=2.0)
print(p.x)     # 3.5
print(p.y)     # 2.0

# Porównanie działa od razu
q = Punkt(3.5, 2.0)
print(p == q)  # True
print(p is q)  # False - różne obiekty

Klasa Punkt ma automatycznie wygenerowany __init__, __repr__ i __eq__. Nie musieliśmy pisać ani jednej metody. Możemy też dodać własne metody, np. do obliczania odległości między punktami, które uzupełnią wygenerowany kod.

@dataclass
class Punkt:
    x: float
    y: float

    def odleglosc_do(self, inny: "Punkt") -> float:
        return ((self.x - inny.x)**2 + (self.y - inny.y)**2)**0.5

p1 = Punkt(0.0, 0.0)
p2 = Punkt(3.0, 4.0)
print(p1.odleglosc_do(p2))  # 5.0
Pierwsza dataclass

Temat Kod: pierwsza dataclass stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

9 Kod: automatyczny __init__ i __repr__
@dataclass
class Punkt:
    x: float
    y: float

p = Punkt(1.0, 2.0)
print(repr(p))  # Punkt(x=1.0, y=2.0)
print(p)          # też Punkt(x=1.0, y=2.0) – __str__ = __repr__

# Możemy też podejrzeć, co wygenerował dataclass
print(type(p).__init__)
# 
print(type(p).__repr__)
# 

# Użyjmy inspect, by zobaczyć wygenerowany kod
import inspect
print(inspect.getsource(Punkt.__init__))

__init__ przyjmuje argumenty w kolejności zdefiniowanych pól. __repr__ zwraca czytelny napis z nazwami pól i ich wartościami, co jest nieocenione podczas debugowania. Warto zauważyć, że w dataclass __str__ domyślnie nie jest generowany osobno — Python używa wtedy __repr__ również do wyświetlania przez print(). Możemy jednak zdefiniować własne __str__, które nadpisze to zachowanie.

__init__ i __repr__

Temat Kod: automatyczny __init__ i __repr__ stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

10 Kod: automatyczny __eq__
@dataclass
class Punkt:
    x: float
    y: float

p1 = Punkt(1.0, 2.0)
p2 = Punkt(1.0, 2.0)
p3 = Punkt(3.0, 4.0)

print(p1 == p2)  # True
print(p1 == p3)  # False
print(p1 is p2)  # False (różne obiekty w pamięci)

# Porównanie z innym typem
print(p1 == "Punkt(1.0, 2.0)")  # False (inny typ)

# Można też porównywać z krotką? Nie – typ musi być zgodny
# print(p1 == (1.0, 2.0))  # False

__eq__ porównuje wszystkie pola (w kolejności definicji). Dwa obiekty są równe, gdy wszystkie ich pola są równe. To dużo wygodniejsze niż ręczne pisanie. Ważne: __eq__ zwraca NotImplemented dla obiektów innego typu, co pozwala Pythonowi sprawdzić, czy druga strona porównania ma własną implementację __eq__.

__eq__

Temat Kod: automatyczny __eq__ stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

11 Podsumowanie — wprowadzenie

Co już wiemy?

  • @dataclass automatyzuje tworzenie klas danych — wystarczy zdefiniować pola z typami
  • Generuje __init__, __repr__, __eq__ automatycznie, a przy order=True również metody porównawcze
  • Wystarczy zdefiniować pola z adnotacjami typów — Python sam określa kolejność i typy argumentów
  • Kod jest krótszy, czytelniejszy i mniej podatny na błędy — typowa klasa to 5 linii zamiast 30
  • Wbudowany moduł — nie wymaga instalacji, dostępny od Pythona 3.7
  • Można łączyć podejście automatyczne z własnymi metodami — wszystko co napiszemy ręcznie ma pierwszeństwo
Zapamiętaj: dataclass to nie magiczne narzędzie — to generator kodu, który pisze za Ciebie to, co i tak byś napisał. Ale robi to szybciej, poprawniej i spójniej.

W kolejnych slajdach przejdziemy do szczegółowych opcji dekoratora @dataclass, zaawansowanej konfiguracji pól za pomocą field(), oraz praktycznych zastosowań takich jak klasy niemutowalne i sortowanie.

Podsumowanie

Wprowadzenie do nowego tematu zawsze rozpoczynamy od przedstawienia kontekstu i uzasadnienia, dlaczego dane zagadnienie jest istotne w praktyce programistycznej. Każdy moduł kursu został zaprojektowany tak, aby stopniowo budować zrozumienie - od prostych analogii po konkretne implementacje w kodzie. Przedstawione koncepcje są fundamentem, na którym opierają się bardziej zaawansowane zagadnienia w kolejnych częściach kursu. Zaleca się aktywne zapoznanie z materiałem poprzez samodzielne testowanie przykładów i modyfikowanie ich w celu lepszego zrozumienia działania. Systematyczna praca i regularne powtórki są kluczem do sukcesu w nauce. Zachęcamy do korzystania z dodatkowych źródeł, takich jak oficjalna dokumentacja Pythona.

W tej części kursu skupimy się zarówno na teorii, jak i na praktycznych przykładach, które pozwolą na pełne zrozumienie omawianego zagadnienia. Każdy slajd zawiera elementy interaktywne, które zachęcają do samodzielnego eksperymentowania z kodem. Warto poświęcić czas na dokładne przeanalizowanie każdego przykładu i zrozumienie, dlaczego kod działa w określony sposób. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każdy komunikat błędu to cenna lekcja. Ćwiczenia praktyczne pomogą utrwalić zdobytą wiedzę i przygotować się do bardziej złożonych zadań.

12 Dekorator @dataclass

Automatyczne generowanie metod

Dekorator @dataclass przyjmuje parametry, które kontrolują, które metody zostaną wygenerowane. Wszystkie parametry są opcjonalne i mają wartości domyślne:

  • init=True — czy generować __init__. Gdy ustawimy na False, musimy sami zdefiniować konstruktor (rzadki przypadek).
  • repr=True — czy generować __repr__. Przydatne, gdy chcemy własną reprezentację lub gdy pole zawiera poufne dane.
  • eq=True — czy generować __eq__. Domyślnie włączone; porównanie strukturalne po wszystkich polach.
  • order=False — czy generować __lt__, __le__, __gt__, __ge__. Gdy True, wymaga również eq=True.
  • frozen=False — czy obiekty mają być niemutowalne. Gdy True, warto rozważyć również order=True.
  • unsafe_hash=False — czy wymusić generowanie __hash__ nawet gdy eq=True i frozen=False.

Dzięki temu mamy pełną kontrolę nad tym, jakie metody są generowane. To szczególnie ważne w zaawansowanych scenariuszach, np. gdy chcemy zachować kompatybilność z istniejącym kodem.

Dekorator @dataclass

Temat Dekorator @dataclass stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

13 Kod: @dataclass z różnymi opcjami
from dataclasses import dataclass

# Bez __init__ (nietypowe, ale możliwe)
@dataclass(init=False)
class Konfiguracja:
    debug: bool = False
    def __init__(self, debug=False):
        self.debug = debug
        self._zainicjalizowano = True  # dodatkowa logika

# Tylko __init__ i __eq__, bez __repr__
@dataclass(repr=False)
class Dane:
    wartosc: int

d = Dane(42)
print(d)        # <__main__.Dane object at 0x...>  – brak własnego __repr__
print(d == Dane(42))  # True – __eq__ działa
Różne opcje

Temat Kod: @dataclass z różnymi opcjami stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

14 Kod: co generuje dataclass
from dataclasses import dataclass

@dataclass
class Punkt:
    x: float
    y: float

# Sprawdźmy, co wygenerował dataclass
print(type(Punkt.__init__))  # 
print(type(Punkt.__repr__))  # 
print(type(Punkt.__eq__))    # 

# Można też zobaczyć źródło (jeśli mamy dostęp)
import inspect
print(inspect.getsource(Punkt.__init__))
# def __init__(self, x: float, y: float) -> None:
#     self.x = x
#     self.y = y

# Uwaga: dataclass NIE generuje __str__ ani __hash__ domyślnie
print(hasattr(Punkt, "__str__"))  # True – dziedziczony z object
print(hasattr(Punkt, "__hash__"))  # True – ale ustawiony na None (obiekty niehaszowalne)
Co generuje

Temat Kod: co generuje dataclass stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

15 Podsumowanie — dekorator

Kluczowe informacje

  • @dataclass to dekorator klasy, nie funkcja — stosujemy go przed definicją klasy
  • Domyślnie generuje __init__, __repr__, __eq__ — trzy najczęsciej potrzebne metody
  • Parametry pozwalają wyłączyć poszczególne metody — init=False, repr=False, eq=False
  • Można też wygenerować __lt__, __le__, __gt__, __ge__ (parametr order) — wszystkie cztery jednocześnie
  • Można tworzyć klasy niemutowalne (frozen) — wtedy __hash__ jest automatycznie generowany
  • Dodatkowy parametr unsafe_hash pozwala na kontrolę nad generowaniem __hash__ w niestandardowych sytuacjach
Podsumowanie

Temat Podsumowanie — dekorator stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

16 Pola z typowaniem (type hints)

Typy pól w dataclass

Każde pole w dataclass musi mieć adnotację typu, by być traktowane jako pole. Pola bez adnotacji są pomijane przez dekorator — stają się zwykłymi atrybutami klasy.

Adnotacje pełnią dwie role:

  • Dokumentacyjną — mówią programiście i narzędziom (IDE, mypy), jakiego typu są dane
  • Funkcyjnądataclass używa ich do określenia kolejności pól w __init__ oraz do powiązania domyślnych wartości z polami
Ważne: dataclass nie wymusza typów w runtime — to tylko informacja dla programisty i narzędzi (mypy, IDE). Możesz przekazać str do pola zadeklarowanego jako int i Python nie zaprotestuje. Do sprawdzania typów w runtime używaj osobnych rozwiązań (np. pydantic).

Od Pythona 3.10 można używać składni X | None zamiast Optional[X]. Od Pythona 3.9 zamiast List[int] z modułu typing można używać list[int] bezpośrednio.

Typowanie pól

Temat Pola z typowaniem (type hints) stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

17 Kod: str, int, float, list
from dataclasses import dataclass
from typing import List

@dataclass
class Student:
    imie: str
    wiek: int
    srednia: float
    oceny: List[int]

# Użycie
s = Student("Anna", 22, 4.75, [5, 5, 4, 5])
print(s)
# Student(imie='Anna', wiek=22, srednia=4.75, oceny=[5, 5, 4, 5])

# Python 3.9+: można użyć list[int] zamiast typing.List[int]
@dataclass
class StudentNowy:
    imie: str
    wiek: int
    srednia: float
    oceny: list[int]       # prostsza składnia
    adres_email: str | None = None  # Python 3.10+ składnia unii
Typy pól

Temat Kod: str, int, float, list stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

18 Kod: Optional i inne typy
from dataclasses import dataclass
from typing import Optional, List, Dict, Tuple

@dataclass
class Pracownik:
    imie: str
    stanowisko: str
    pensja: float
    email: Optional[str] = None
    projekty: List[str] = field(default_factory=list)
    metadane: Dict[str, str] = field(default_factory=dict)

# Tworzenie obiektu – tylko wymagane pola
p = Pracownik("Jan", "Developer", 8000.0)
print(p.email)    # None
print(p.projekty) # []
print(p.metadane) # {}

# Tworzenie z wszystkimi polami
p2 = Pracownik(
    imie="Anna",
    stanowisko="Manager",
    pensja=12000.0,
    email="anna@firma.pl",
    projekty=["Projekt A", "Projekt B"],
    metadane={"dział": "IT"}
)
Optional i inne

Temat Kod: Optional i inne typy stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

19 Podsumowanie typowania

Co warto wiedzieć o typach

  • Adnotacje typów są obowiązkowe w dataclass — pola bez adnotacji nie są traktowane przez dekorator
  • Typy nie są wymuszane w runtime — to tylko wskazówki dla programisty, IDE i narzędzi zewnętrznych
  • Można używać str, int, float, bool, list, dict, tuple oraz typów złożonych
  • Do złożonych typów używamy modułu typing: List, Dict, Tuple, Optional, Set, FrozenSet
  • Optional[X] to skrót od Union[X, None] — oznacza, że pole może być typu X lub None
  • Python 3.9+: zamiast List[int] można pisać list[int] (to samo dla dict, tuple, set)
  • Python 3.10+: zamiast Optional[str] można pisać str | None (składnia unii typów)
Podsumowanie typów

Temat Podsumowanie typowania stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

20 Domyślne wartości pól

Wartości domyślne w dataclass

Możemy ustawić domyślne wartości dla pól. Ważna zasada: pola z wartościami domyślnymi muszą występować po polach bez wartości domyślnych (tak jak w funkcjach). Jest to wymóg składniowy Pythona — jeśli go naruszymy, otrzymamy SyntaxError.

@dataclass
class Adres:
    ulica: str
    miasto: str
    kod: str = "00-001"
    kraj: str = "Polska"

# Tworzenie obiektu
adres1 = Adres("Główna 1", "Kraków")
adres2 = Adres("Mickiewicza 5", "Warszawa", "00-950")
adres3 = Adres("Szeroka 3", "Gdańsk", kraj="Niemcy")
# adres4 = Adres("Polna 2")  # Błąd! ulica i miasto są wymagane

Dzięki domyślnym wartościom możemy tworzyć obiekty z różnym poziomem szczegółowości — podajemy tylko to, co chcemy zmienić.

Wartości domyślne

Temat Domyślne wartości pól stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

21 Kod: pola z domyślnymi wartościami
@dataclass
class Produkt:
    nazwa: str
    cena: float
    vat: float = 0.23
    dostepny: bool = True
    opis: str = ""

# Różne sposoby tworzenia
p1 = Produkt("Laptop", 3500.0)
p2 = Produkt("Monitor", 1200.0, vat=0.08)
p3 = Produkt("Mysz", 89.99, dostepny=False)

print(p1)  # Produkt(nazwa='Laptop', cena=3500.0, vat=0.23, dostepny=True, opis='')
print(p2)  # Produkt(nazwa='Monitor', cena=1200.0, vat=0.08, dostepny=True, opis='')
print(p3)  # Produkt(nazwa='Mysz', cena=89.99, vat=0.23, dostepny=False, opis='')

# Obliczenie ceny brutto
def cena_brutto(produkt: Produkt) -> float:
    return produkt.cena * (1 + produkt.vat)

print(cena_brutto(p1))  # 4305.0
Domyślne wartości

Temat Kod: pola z domyślnymi wartościami stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

22 Kod: uwaga na mutowalne domyślne

Pułapka: mutowalne domyślne

# ŹLE! Wszystkie obiekty współdzielą tę samą listę!
@dataclass
class Koszyk:
    przedmioty: list = []  # Błąd: ValueError

Python zabrania używania mutowalnych domyślnych wartości w dataclass. Gdyby to działało, wszystkie obiekty współdzieliłyby tę samą listę — klasyczny problem domyślnych argumentów mutowalnych. Zamiast tego używamy field(default_factory=...):

from dataclasses import field

@dataclass
class Koszyk:
    przedmioty: list = field(default_factory=list)

# Każdy obiekt ma teraz własną listę
k1 = Koszyk()
k2 = Koszyk()
k1.przedmioty.append("jabłko")
print(k1.przedmioty)  # ['jabłko']
print(k2.przedmioty)  # [] – osobna lista!
Mutowalne domyślne

Temat Kod: uwaga na mutowalne domyślne stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

23 Podsumowanie — domyślne wartości

Zasady domyślnych wartości

  • Pola z domyślnymi wartościami muszą być na końcu listy pól (zasada taka jak w funkcjach)
  • Nie można używać mutowalnych domyślnych (list, dict, set) — Python rzuci ValueError podczas definiowania klasy
  • Zamiast tego używamy field(default_factory=list) — fabryka jest wołana raz na każdą instancję
  • Domyślne wartości mogą być dowolnego typu (str, int, float, bool, None) — ważne, by były niemutowalne
  • Wartością domyślną może być również None dla pól opcjonalnych — wtedy w __post_init__ możemy ustawić właściwą wartość
Wskazówka: Jeśli potrzebujesz pustej listy jako domyślnej, zawsze używaj default_factory=list. Dla pustego słownika: default_factory=dict. Dla pustego zbioru: default_factory=set.
Podsumowanie

Temat Podsumowanie — domyślne wartości stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

24 field() — zaawansowana konfiguracja

Funkcja field()

field() pozwala precyzyjnie kontrolować każde pole dataclass z osobna. Jest to potężne narzędzie, gdy potrzebujemy większej kontroli niż oferują domyślne wartości. Oferuje parametry takie jak:

  • default — domyślna wartość (dla typów niemutowalnych, np. str, int)
  • default_factory — funkcja (bez argumentów) generująca domyślną wartość dla każdej nowej instancji
  • init — czy uwzględniać w __init__ (gdy False, pole musi mieć wartość domyślną)
  • repr — czy uwzględniać w __repr__ (przydatne dla pól z poufnymi danymi)
  • compare — czy uwzględniać w porównaniach (__eq__, __lt__, itd.)
  • hash — czy uwzględniać w __hash__ (może być None — wtedy używa wartości z compare)
  • metadata — słownik z dodatkowymi informacjami (np. dla walidatorów, generatorów formularzy)
field()

Temat field() — zaawansowana konfiguracja stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

25 Kod: default_factory, init=False
from dataclasses import dataclass, field
import datetime

@dataclass
class Zamowienie:
    produkt: str
    cena: float
    ilosc: int = 1
    # default_factory – nowa lista dla każdej instancji
    tagi: list = field(default_factory=list)
    # init=False – pole nie pojawi się w __init__
    data_utworzenia: datetime.datetime = field(default_factory=datetime.datetime.now, init=False)

# __init__ przyjmuje tylko: produkt, cena, ilosc, tagi
z = Zamowienie("Laptop", 3500.0)
print(z.produkt)          # Laptop
print(z.data_utworzenia)  # 2024-... (automatycznie ustawione)

# Każde zamówienie ma inną datę utworzenia
import time
time.sleep(0.1)
z2 = Zamowienie("Monitor", 1200.0)
print(z.data_utworzenia < z2.data_utworzenia)  # True

# init=False – nie można ustawić przez konstruktor
# z3 = Zamowienie("Mysz", 100.0, data_utworzenia=...)  # TypeError!
default_factory init=False

Temat Kod: default_factory, init=False stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

26 Kod: repr=False, compare=False
from dataclasses import dataclass, field

@dataclass
class Uzytkownik:
    nazwa: str
    email: str
    # hasło nie będzie widoczne w repr – bezpieczeństwo
    haslo: str = field(repr=False)
    # id nie będzie używane w porównaniach – tylko zawartość merytoryczna
    id: int = field(compare=False, default=0)

u1 = Uzytkownik("ala", "ala@test.pl", "tajne123", id=1)
u2 = Uzytkownik("ala", "ala@test.pl", "inne123", id=2)
print(u1)
# Uzytkownik(nazwa='ala', email='ala@test.pl', id=1)  # brak hasla!
print(u1 == u2)  # True – id i haslo nie są porównywane, tylko nazwa i email

# Dwa obiekty z różnym id ale taką samą nazwą i emailem są równe
u3 = Uzytkownik("ala", "ala@test.pl", "cokolwiek", id=999)
print(u1 == u3)  # True – tylko nazwa i email liczą się w porównaniu
repr=False compare=False

Temat Kod: repr=False, compare=False stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

27 Podsumowanie field()

Kiedy używać field()

  • Gdy potrzebujesz mutowalnej domyślnej wartości — default_factory (np. list, dict, set)
  • Gdy pole nie powinno być ustawiane przez konstruktor — init=False (np. znacznik czasu utworzenia)
  • Gdy pole nie powinno być widoczne w reprezentacji — repr=False (np. hasło, token, sekret)
  • Gdy pole nie powinno być uwzględniane w porównaniach — compare=False (np. identyfikator w bazie danych)
  • Gdy potrzebujesz precyzyjnej kontroli nad hashowaniem — hash=False lub hash=None
  • Gdy chcesz dołączyć dodatkowe metadane do pola — metadata={...} (np. dla walidatorów zewnętrznych)
Uwaga: field(default=[]) wciąż jest błędem! Używaj default_factory=list.

Dzięki field() mamy pełną kontrolę nad każdym polem z osobna, nie tracąc przy tym wygody automatycznego generowania kodu przez @dataclass.

Podsumowanie field

Temat Podsumowanie field() stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

28 Porównywanie dataclass

Domyślne __eq__

Każda dataclass domyślnie ma zdefiniowane __eq__, które porównuje wszystkie pola (w kolejności definicji). Dwa obiekty są równe, gdy wszystkie ich pola są równe. To podejście nazywamy porównaniem strukturalnym — liczy się wartość pól, nie tożsamość obiektów w pamięci.

Jeśli chcemy porównywać tylko wybrane pola, używamy field(compare=False) dla pozostałych. To przydatne, gdy mamy pole, które nie wpływa na równość logiczną obiektów (np. identyfikator w bazie danych, znacznik czasu).

Uwaga dotycząca __hash__:

  • Gdy eq=True i frozen=False (domyślnie): __hash__ = None (obiekty niehaszowalne)
  • Gdy eq=True i frozen=True: __hash__ jest generowane (obiekty haszowalne)
  • Gdy eq=False: __hash__ dziedziczone z object (oparte na tożsamości)
  • Można wymusić __hash__ przez unsafe_hash=True
Porównywanie

Temat Porównywanie dataclass stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

29 Kod: porównanie dwóch dataclass
@dataclass
class Osoba:
    imie: str
    nazwisko: str
    wiek: int

a = Osoba("Jan", "Kowalski", 30)
b = Osoba("Jan", "Kowalski", 30)
c = Osoba("Anna", "Nowak", 25)

print(a == b)   # True – te same wartości pól
print(a == c)   # False – różne wartości
print(a is b)   # False (różne obiekty w pamięci)

# Porównanie z innym typem
print(a == "Jan Kowalski")  # False – inny typ

# Porównanie z użyciem w słowniku
# Uwaga: dataclass bez frozen nie ma __hash__, więc nie może być kluczem
# slownik = {a: "pierwszy"}  # TypeError!
Porównanie kod

Temat Kod: porównanie dwóch dataclass stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

30 Kod: sortowanie
@dataclass(order=True)
class Pracownik:
    nazwisko: str
    pensja: float

p1 = Pracownik("Kowalski", 5000)
p2 = Pracownik("Nowak", 6000)
p3 = Pracownik("Adamek", 4500)

print(p1 < p2)   # True (Kowalski < Nowak alfabetycznie)
print(p3 <= p1)  # True (Adamek <= Kowalski)
print(p1 > p3)   # True
print(p2 >= p1)  # True

# Sortowanie listy – według kolejności pól: najpierw nazwisko, potem pensja
lista = [p1, p2, p3]
lista.sort()
print(lista)
# [Pracownik(nazwisko='Adamek', pensja=4500),
#  Pracownik(nazwisko='Kowalski', pensja=5000),
#  Pracownik(nazwisko='Nowak', pensja=6000)]

# Sortowanie po wybranym polu (bez order=True)
# sorted(lista, key=lambda p: p.pensja)  # niezależnie od @dataclass(order=True)
Sortowanie

Temat Kod: sortowanie stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

31 frozen=True — zamrożone klasy

Niemutowalność (immutability)

Parametr frozen=True sprawia, że obiekty klasy są niemutowalne — nie można zmienić wartości pól po utworzeniu obiektu. Próba modyfikacji skutkuje błędem FrozenInstanceError (podklasa AttributeError).

Zalety:

  • Bezpieczeństwo — obiekt nie zmieni się nieoczekiwanie w trakcie działania programu
  • Można używać jako kluczy w słownikach lub elementów zbiorów (jeśli eq=True)
  • Łatwiejsze wnioskowanie o kodzie — stan obiektu jest stały od momentu utworzenia
  • Współbieżność — brak problemów z dostępem do współdzielonych, mutowalnych stanów

Wady:

  • Nie wszystkie problemy dadzą się modelować niemutowalnie
  • Aby zmienić obiekt, trzeba utworzyć nowy (można użyć dataclasses.replace())
  • Może być mniej wydajne przy częstych zmianach
frozen=True

Temat frozen=True — zamrożone klasy stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

32 Kod: @dataclass(frozen=True)
from dataclasses import dataclass

@dataclass(frozen=True)
class Punkt3D:
    x: float
    y: float
    z: float

p = Punkt3D(1.0, 2.0, 3.0)
print(p.x)  # 1.0

# Obiekt jest hashowalny – może byc kluczem w słowniku
d = {p: "punkt A"}
print(d[p])  # punkt A

# Można też używać w zbiorach
zbior = {Punkt3D(0, 0, 0), Punkt3D(1, 1, 1)}
print(zbior)

# Tworzenie zmodyfikowanej kopii za pomocą replace
from dataclasses import replace
p2 = replace(p, x=10.0)
print(p2)  # Punkt3D(x=10.0, y=2.0, z=3.0)
# p pozostaje bez zmian
frozen kod

Temat Kod: @dataclass(frozen=True) stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

33 Kod: próba modyfikacji (błąd)
@dataclass(frozen=True)
class Punkt3D:
    x: float
    y: float
    z: float

p = Punkt3D(1.0, 2.0, 3.0)

# Próba modyfikacji – błąd!
try:
    p.x = 10.0
except Exception as e:
    print(type(e).__name__)  # FrozenInstanceError
    print(e)
    # cannot assign to field 'x'

# Aby zmienić, trzeba utworzyć nowy obiekt
p2 = Punkt3D(10.0, p.y, p.z)
print(p2)  # Punkt3D(x=10.0, y=2.0, z=3.0)

# Lub użyć replace() – wygodniejsze przy wielu polach
from dataclasses import replace
p3 = replace(p, x=10.0)
print(p3)  # Punkt3D(x=10.0, y=2.0, z=3.0)
# p nadal: Punkt3D(x=1.0, y=2.0, z=3.0)
Błąd modyfikacji

Temat Kod: próba modyfikacji (błąd) stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

34 Kiedy używać frozen

Praktyczne wskazówki

  • Używaj frozen=True, gdy:
    • Obiekt reprezentuje wartość (np. punkt, kolor, konfiguracja, współrzędne geograficzne)
    • Chcesz używać obiektu jako klucza w słowniku lub elementu zbioru (set)
    • Chcesz mieć pewność, że obiekt nie zmieni się po utworzeniu (niezmienniczość)
    • Piszesz kod współbieżny — niemutowalne obiekty są bezpieczne wątków
    • Implementujesz wzorzec DTO (Data Transfer Object)
  • Unikaj frozen, gdy:
    • Obiekt ma wiele pól, które często się zmieniają (np. stan sesji użytkownika)
    • Potrzebujesz mutowalności z natury rzeczy (np. bufor, kolekcja robocza)
    • Tworzysz obiekt tymczasowy, który jest intensywnie modyfikowany
Złota zasada: Jeśli nie masz pewności, zacznij bez frozen. Możesz dodać później — zmiana z mutowalnego na niemutowalny to tylko dodanie parametru w dekoratorze.
Kiedy frozen

Temat Kiedy używać frozen stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

35 order=True — sortowanie

Automatyczne porównania

Parametr order=True generuje wszystkie metody porównania:

  • __lt__ — mniejszy niż (<)
  • __le__ — mniejszy lub równy (<=)
  • __gt__ — większy niż (>)
  • __ge__ — większy lub równy (>=)

Porównanie odbywa się przez wszystkie pola, w kolejności definicji (tak jak w krotce). Oznacza to, że najpierw porównywane jest pierwsze pole; dopiero gdy jest równe, porównywane jest drugie, itd. To zachowanie jest analogiczne do sortowania leksykograficznego krotek.

Wymagania dla order=True:

  • eq musi być True (to domyślna wartość)
  • Wszystkie pola muszą być wzajemnie porównywalne (mieć zdefiniowane odpowiednie operatory)
order=True

Temat order=True — sortowanie stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

36 Kod: @dataclass(order=True)
@dataclass(order=True)
class Film:
    tytul: str
    rok: int
    ocena: float

f1 = Film("Memento", 2000, 8.5)
f2 = Film("Incepcja", 2010, 8.8)
f3 = Film("Prestiż", 2006, 8.5)

print(f1 < f2)
# Porównanie: tytul 'Memento' < 'Incepcja'? False (M > I)

print(f1 > f3)
# Porównanie: tytul 'Memento' > 'Prestiż'? False (M < P)

print(f2 > f3)
# Porównanie: tytul 'Incepcja' > 'Prestiż'? False (I < P)

# Uwaga: sortowanie najpierw po tytule!
# Jeśli tytuly są takie same, dopiero wtedy po roku, potem po ocenie
order kod

Temat Kod: @dataclass(order=True) stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

37 Kod: sortowanie listy obiektów
@dataclass(order=True)
class Film:
    tytul: str
    rok: int
    ocena: float

filmy = [
    Film("Memento", 2000, 8.5),
    Film("Incepcja", 2010, 8.8),
    Film("Prestiż", 2006, 8.5),
    Film("Czarny łabędź", 2010, 8.0),
]

# Sortowanie według kolejności pól: tytul, rok, ocena
filmy_posortowane = sorted(filmy)
for f in filmy_posortowane:
    print(f)
# Uwaga: sortowanie najpierw po tytule, potem po roku, potem po ocenie
# Wynik: Czarny łabędź, Incepcja, Memento, Prestiż

# Sortowanie po wybranej właściwości (np. rok)
po_roku = sorted(filmy, key=lambda f: f.rok)
print("Po roku:")
for f in po_roku:
    print(f)

# Sortowanie po wielu kluczach (rok, potem ocena malejąco)
po_roku_i_ocenie = sorted(filmy, key=lambda f: (f.rok, -f.ocena))
print("Po roku i ocenie:")
for f in po_roku_i_ocenie:
    print(f)
Sortowanie listy

Temat Kod: sortowanie listy obiektów stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

38 Praktyczny przykład: Adres, Osoba
from dataclasses import dataclass, field
from typing import List

@dataclass
class Adres:
    ulica: str
    miasto: str
    kod: str = "00-001"
    kraj: str = "Polska"

    def __str__(self) -> str:
        return f"{self.ulica}, {self.kod} {self.miasto}, {self.kraj}"

@dataclass
class Osoba:
    imie: str
    nazwisko: str
    wiek: int
    email: str
    adres: Adres = field(default_factory=lambda: Adres("", ""))
    zainteresowania: list = field(default_factory=list)

    def przedstaw_sie(self) -> str:
        return f"Jestem {self.imie} {self.nazwisko}, mam {self.wiek} lat."

To jest przykład kompozycji — jedna dataclass (Adres) jest polem w innej dataclass (Osoba). Dzięki default_factory, każda nowa osoba może mieć domyślny pusty adres bez ryzyka współdzielenia referencji.

Adres Osoba

Temat Praktyczny przykład: Adres, Osoba stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

39 Kod: testowanie
# Tworzenie obiektów
adres = Adres("Główna 1", "Kraków", "31-001")
osoba = Osoba(
    imie="Anna",
    nazwisko="Kowalska",
    wiek=28,
    email="anna@test.pl",
    adres=adres,
    zainteresowania=["Python", "AI"]
)

print(osoba)
# Osoba(imie='Anna', nazwisko='Kowalska', wiek=28, email='anna@test.pl',
#       adres=Adres(ulica='Główna 1', miasto='Kraków', kod='31-001',
#       kraj='Polska'), zainteresowania=['Python', 'AI'])

# Użycie własnej metody
print(osoba.przedstaw_sie())  # Jestem Anna Kowalska, mam 28 lat.

# Użycie __str__ z Adres
print(adres)  # Główna 1, 31-001 Kraków, Polska

# Porównanie
osoba2 = Osoba("Anna", "Kowalska", 28, "anna@test.pl", adres)
print(osoba == osoba2)  # True – te same dane

# Konwersja na słownik i krotkę
from dataclasses import asdict, astuple
print(asdict(osoba))
# {'imie': 'Anna', 'nazwisko': 'Kowalska', 'wiek': 28, ...}
print(astuple(osoba))
# ('Anna', 'Kowalska', 28, 'anna@test.pl', Adres(...), ['Python', 'AI'])
Testowanie

Temat Kod: testowanie stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.

40 Podsumowanie i mapa myśli

Co poznaliśmy w tej części?

Mapa myśli — Klasy danych (dataclass)
  • @dataclass — dekorator do automatycznego generowania metod (__init__, __repr__, __eq__)
  • Pola z typami — str, int, float, list, Optional, Dict, Tuple; adnotacje obowiązkowe ale niewymuszane w runtime
  • Domyślne wartości — zwykłe (dla typów niemutowalnych) i z default_factory (dla list, dict, set)
  • field() — zaawansowana konfiguracja (init, repr, compare, hash, metadata, default_factory)
  • frozen=True — klasy niemutowalne, hashowalne, bezpieczne wątkowo
  • order=True — automatyczne sortowanie przez wszystkie metody porównania
  • Kompozycja — dataclass wewnątrz dataclass (np. Adres wewnątrz Osoba)
  • Funkcje pomocniczeasdict(), astuple(), replace(), fields(), __post_init__()
  • Porównanie z alternatywamidataclass łączy elastyczność klas z wygodą namedtuple

To potężne narzędzie, które znacząco redukuje boilerplate i czyni kod bardziej czytelnym. Stosuj je zawsze, gdy tworzysz klasy przechowujące dane — w praktyce nawet 80% klas w typowej aplikacji może być zaimplementowanych jako dataclass.

Podsumowanie

Temat Podsumowanie i mapa myśli stanowi ważny element omawianego w tej części zagadnienia. Zrozumienie przedstawionych tu koncepcji jest niezbędne do pełnego opanowania programowania obiektowego w języku Python. W praktyce programistycznej omawiane mechanizmy są wykorzystywane codziennie przez tysiące programistów na całym świecie. Prezentowane przykłady zostały starannie dobrane, aby ilustrować zarówno podstawowe, jak i zaawansowane zastosowania. Zaleca się samodzielne przetestowanie każdego przykładu i eksperymentowanie z modyfikacjami kodu. Systematyczne podejście do nauki przynosi najlepsze efekty w długoterminowej perspektywie.

W codziennej pracy programisty znajomość omawianego tematu pozwala na pisanie bardziej czytelnego, efektywnego i łatwiejszego w utrzymaniu kodu. Współczesne standardy programowania wymagają od programistów biegłości w stosowaniu tych mechanizmów. Dlatego tak ważne jest poświęcenie odpowiedniej ilości czasu na dokładne zrozumienie prezentowanego materiału. Zachęcamy do powracania do trudniejszych fragmentów i korzystania z dodatkowych źródeł wiedzy. Pamiętaj, że nauka programowania to proces ciągły, a każdy nowo opanowany temat przybliża Cię do mistrzostwa w zawodzie.