Masz kilka zadań do wykonania, a każde ma własny status, dane i metodę zmiany stanu. Klasa w Pythonie zwykle dobrze porządkuje taki kod, choć przy jednym prostym rekordzie słownik może być lżejszym wyborem.

Klasa łączy stan obiektu z operacjami, które na nim wykonujesz
- Klasa opisuje wspólną strukturę i zachowanie obiektów.
- Instancja to pojedynczy obiekt utworzony na podstawie klasy.
- self wskazuje instancję, na której działa metoda.
- Dataclass ogranicza powtarzalny kod, gdy obiekt głównie przechowuje dane.
- Słownik często wystarczy, gdy struktura danych jest mała lub zmienna.
Klasa opisuje typ obiektu, a instancja przechowuje własny stan
Wyobraź sobie dwa zadania o tym samym zestawie pól: nazwie i statusie. Klasa opisuje, jakie dane i operacje mają być dostępne, a każda utworzona instancja przechowuje własne wartości.
W definicji zapisujesz metody, czyli funkcje związane z klasą. Gdy wywołujesz metodę przez obiekt, Python przekazuje tę instancję jako pierwszy argument. Dlatego job.finish() można rozumieć jako wywołanie Job.finish(job), co opisuje oficjalny samouczek Pythona.
Ten mechanizm sprawia, że metoda może zmieniać stan właściwego obiektu. Bez tej więzi łatwo skończyć z funkcjami, którym za każdym razem trzeba przekazywać ten sam zestaw powiązanych danych.
Wystarczą trzy elementy, żeby utworzyć i użyć obiektu
Najprostsza klasa potrzebuje nazwy, metody __init__ ustawiającej początkowe dane oraz metody opisującej działanie obiektu. W przykładzie zadanie zmienia status z „oczekujące” na „gotowe”.
class Job:
def __init__(self, name):
self.name = name
self.status = "oczekujące"
def finish(self):
self.status = "gotowe"
job = Job("Raport")
job.finish()
print(job.name) # Raport
print(job.status) # gotoweself nie jest specjalnym słowem kluczowym. To powszechnie używana nazwa pierwszego argumentu metody, którą opisuje FAQ dokumentacji Pythona. Mimo że technicznie można wybrać inną nazwę, trzymaj się self, bo dzięki temu kod jest czytelny dla innych osób.
Jest tu też pułapka, która ujawnia się dopiero przy kilku obiektach. Jeśli lista miałaby być atrybutem klasy, a nie instancji, wszystkie obiekty mogłyby współdzielić tę samą listę:
class Job:
tags = [] # wspólna lista dla instancji tej klasyGdy każda instancja ma mieć osobne tagi, twórz listę w __init__: self.tags = []. Atrybuty zmienne, takie jak listy i słowniki, szczególnie łatwo przypadkiem współdzielić.
Wybierz klasę wtedy, gdy dane i metody tworzą jeden model
Jeśli masz tylko chwilowy zestaw pól, klasa może dołożyć ceremonii zamiast porządku. Praktyczna granica jest prosta: użyj słownika dla danych o zmiennym kształcie, a klasy wtedy, gdy chcesz połączyć ustalone dane z zachowaniem obiektu.
| Sytuacja | Co wybrać | Dlaczego |
|---|---|---|
| Jednorazowe dane, których pola mogą się zmieniać | dict |
Możesz dodawać i usuwać klucze bez zmieniania definicji typu. |
| Stały zestaw pól, głównie do przechowywania danych | @dataclass |
Dekorator generuje część powtarzalnych metod, między innymi inicjalizację i reprezentację obiektu. |
| Obiekt ma pilnować reguł lub wykonywać operacje na swoim stanie | Zwykła klasa | Samodzielnie określasz, jak obiekt powstaje i jak wolno zmieniać jego stan. |
Wbudowany moduł dataclasses pozwala zapisać klasę z polami bez ręcznego tworzenia części typowych metod. Dokumentacja modułu dataclasses opisuje między innymi generowanie inicjalizacji, porównywania i tekstowej reprezentacji. Pamiętaj jednak, że sama adnotacja typu, taka jak name: str, nie zastępuje walidacji danych.
Przyznam, że do prostego obiektu przenoszącego dane wybrałbym dataclass, a do obiektu pilnującego reguł, takiego jak zadanie z dozwolonymi przejściami statusu, zwykłą klasę. Słownik zostawiłbym dla danych, których struktura naprawdę może się zmieniać.
Gdy stan obiektu nie zmienia się poprawnie, sprawdź miejsce przypisania
Dwa obiekty zachowują się tak, jakby były jednym? Najpierw sprawdź, czy zmienna, którą zmieniasz, należy do instancji, czy została zdefiniowana na poziomie klasy.
| Sytuacja | Prawdopodobna przyczyna | Co zrobić |
|---|---|---|
| Zmiana listy w jednym obiekcie wpływa na drugi | Lista jest atrybutem klasy i jest współdzielona. | Utwórz ją w __init__ przez przypisanie do self. |
Wywołanie job.finish() zgłasza błąd brakującego argumentu |
Metoda może nie przyjmować self w definicji. |
Dodaj self jako pierwszy parametr metody instancji. |
| Wartość pola nie pojawia się po utworzeniu obiektu | Przypisanie mogło trafić do zmiennej lokalnej zamiast do atrybutu instancji. | W __init__ przypisz wartość do self.nazwa_pola. |
Jeśli metoda ma działać bez konkretnej instancji, sprawdź, czy potrzebujesz metody klasowej lub statycznej. Nie dodawaj jednak dekoratora tylko po to, żeby ominąć brak self: najpierw ustal, czy operacja dotyczy stanu obiektu, całej klasy, czy nie potrzebuje żadnego z nich.
Traktuj klasę jak narzędzie, nie obowiązkowy etap
Prosty skrypt nie musi zaczynać się od hierarchii klas. Jeśli kilka funkcji stale przyjmuje te same dane i razem pilnują wspólnego stanu, model obiektowy może je uporządkować; przy krótkim przetwarzaniu pojedynczego słownika funkcja będzie zwykle prostsza.
Dobry test: jeśli potrafisz nazwać obiekt rzeczownikiem i wskazać, co może zrobić albo jak zmienia swój stan, klasa prawdopodobnie ma sens. Jeśli potrzebujesz tylko wygodnego pojemnika na stałe pola, rozważ @dataclass; gdy pola są nieprzewidywalne, zacznij od słownika.
