Menu

Klasy w Pythonie: __init__, self, metody, dziedziczenie i dataclasses

Wprowadzenie do klas w Pythonie dla początkujących: definiowanie klas, tworzenie instancji, metoda __init__, dziedziczenie i kiedy lepiej użyć dataclasses.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Klasa łączy dane i to, co z nimi robisz

Klasa pozwala zdefiniować rodzaj rzeczy (użytkownika, konto bankowe, konfigurację, żądanie) oraz wszystko, co ta rzecz powinna umieć. Rzecz to instancja klasy. Działania nazywa się metodami.

Oto najprostsza możliwa klasa:

Po kolei:

  • class User: rozpoczyna definicję klasy. Nazwy klas zapisuje się zwyczajowo w PascalCase.
  • __init__ to inicjalizator. Uruchamia się automatycznie, gdy tworzysz nową instancję. Ustawia atrybuty instancji przez self.whatever = ....
  • greet to metoda. Przez self metoda odwołuje się do instancji, na której ją wywołano.
  • User("Rosa", "rosa@example.com") tworzy instancję. Python wywołuje __init__, ustawiając self na nową instancję i przekazując pozostałe argumenty.
  • user.greet() wywołuje metodę. Python automatycznie przekazuje user jako self.

self to po prostu pierwszy argument

self nie jest słowem kluczowym. To nazwa, jaką nadaje się pierwszemu parametrowi metod instancji, czyli instancji, na której działa metoda. Nazwę można wybrać dowolnie, ale wszyscy używają self, bo tak robią wszyscy.

Każda metoda instancji przyjmuje self jako pierwszy parametr. Dzięki temu metoda wie, z której instancji czytać i którą modyfikować.

Dodawanie zachowania

Metody mogą odczytywać i zmieniać stan:

__repr__ to kolejna metoda specjalna: punkt zaczepienia typu "jak pokazać ten obiekt przy wypisywaniu?". Dobra definicja bardzo się opłaca podczas debugowania.

Atrybuty klasy a atrybuty instancji

Atrybuty zdefiniowane w ciele klasy (poza __init__) są wspólne dla wszystkich instancji. Atrybuty przypisane do self należą do pojedynczej instancji:

Atrybutów klasy używaj do wartości naprawdę wspólnych: stałych, liczników, konfiguracji. Dane konkretnej instancji umieszczaj w __init__.

Dziedziczenie

Klasa może dziedziczyć po innej klasie, dostając za darmo jej metody i atrybuty oraz dodając lub nadpisując to, co trzeba:

super() daje dostęp do klasy nadrzędnej. Często używa się go w __init__, by zainicjalizować atrybuty rodzica przed dodaniem własnych:

Dziedziczenie jest przydatne, ale łatwo go nadużyć. Początkujący programiści Pythona często sięgają po dziedziczenie tam, gdzie kompozycja (przechowywanie obiektu jako atrybutu) byłaby czytelniejsza. Zacznij od płaskiej struktury klas, a dziedziczenie wprowadzaj dopiero przy prawdziwej relacji "jest rodzajem".

Strona o dziedziczeniu w Pythonie idzie dalej: rozszerzanie metody rodzica przez super(), isinstance i issubclass, abstrakcyjne klasy bazowe, wielodziedziczenie i kolejność rozwiązywania metod.

Dataclasses: wygodniejszy wybór dla rekordów

Gdy klasa ma głównie przechowywać kilka atrybutów z porównywaniem i czytelną reprezentacją, @dataclass oszczędza mnóstwo powtarzalnego kodu:

Porównaj to z ręcznym pisaniem __init__, __repr__ i __eq__. Dataclasses to właściwy pierwszy wybór, gdy alternatywą jest class Thing, w której jest tylko __init__ ustawiający atrybuty.

Właściwości: atrybuty obliczane

Czasem chcesz "atrybutu", który tak naprawdę jest obliczany. @property sprawia, że wygląda to jak zwykły dostęp do atrybutu:

Używaj właściwości tam, gdzie pojawiłoby się obj.get_something(): zamiana na właściwość sprawia, że API jest bardziej naturalne. Nie ukrywaj za nimi kosztownych operacji, bo użytkownik oczekuje, że dostęp do atrybutu jest szybki.

Prywatne umownie

Python nie ma prawdziwej prywatności. Umownie atrybuty zaczynające się od podkreślenia są przeznaczone do użytku wewnętrznego, a czytelnicy nie powinni ich ruszać spoza klasy:

class Cache:
    def __init__(self):
        self._store = {}

    def put(self, key, value):
        self._store[key] = value

    def get(self, key, default=None):
        return self._store.get(key, default)

Nazwy z podwójnym podkreśleniem (__name) włączają name mangling, które dokleja nazwę klasy do atrybutu. To narzędzie do unikania kolizji przy dziedziczeniu, a nie zabezpieczenie.

Kiedy nie używać klasy

Jeśli masz tylko zestaw powiązanych danych, wystarczy dataclass, zwykły słownik albo named tuple. Jeśli masz tylko jedną funkcję, nie potrzebuje ona klasy. Po klasy sięgaj, gdy masz jednocześnie stan i zachowanie, które są ze sobą związane, zwłaszcza gdy będziesz mieć wiele instancji tego samego rodzaju.

Przykład na koniec

Mały bufor logów przypominający plik:

Zwróć uwagę na __len__, kolejną metodę magiczną. Jej zdefiniowanie sprawia, że działa len(logger), bo len(x) to w rzeczywistości wywołanie x.__len__(). Python ma dziesiątki takich punktów zaczepienia, a poznasz je, gdy będą potrzebne.

Najczęściej zadawane pytania

Czym jest klasa w Pythonie?

Klasa to szablon do tworzenia obiektów, które łączą dane (atrybuty) i zachowanie (metody). class User: definiuje klasę User, a User(...) tworzy instancję. Klasy to główne narzędzie Pythona do modelowania rzeczy, które mają zarówno stan, jak i zachowanie.

Co oznacza self w Pythonie?

self to pierwszy parametr każdej metody instancji. Gdy wywołujesz user.greet(), Python przekazuje instancję user jako self. Wewnątrz metody self.name odwołuje się do atrybutu name tego użytkownika. Nazwa self to konwencja, a nie słowo kluczowe, ale używa jej każdy programista Pythona.

Czy Python jest językiem obiektowym?

Tak, w Pythonie wszystko jest obiektem, łącznie z liczbami, napisami i funkcjami. Możesz programować obiektowo, gdy to pasuje, ale Python cię do tego nie zmusza. Wiele prawdziwych programów w Pythonie używa klas oszczędnie i tam, gdzie się da, woli zwykłe funkcje i struktury danych.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ