Python obsługuje trzy rodzaje metod w definicjach klas: metody instancji, metody klasowe i metody statyczne. Różnią się one sposobem wywołania i dostępem do danych klasy i instancji.
Historia:
Początkowo metody klasowe (te z "self") były jedynym typem zachowania, zakładano, że metody zawsze wywołują zachowanie lub zmieniają/odczytują dane konkretnego obiektu. Później w Pythonie pojawiły się metody klasowe (z "cls"), które dają zachowanie dla całej klasy (np. alternatywne konstruktory), a także metody statyczne, podobne do zwykłych funkcji, ale związane z klasą.
Problemy:
Czasami potrzebna jest wspólna funkcjonalność dla wszystkich instancji (metody statyczne). Czasami operacja powinna być dostępna "dla całej klasy" (na przykład tworzenie instancji). Przy tym, jeśli niewłaściwie zdefiniujesz typ metody — możesz uzyskać błędy (na przykład przypadkowo próbując zmienić klasę za pomocą metody instancji, lub odwrotnie).
Rozwiązanie:
Przykład kodu:
class MyClass: def instance_method(self): return f"Instancja: {self}" @classmethod def class_method(cls): return f"Klasa: {cls}" @staticmethod def static_method(): return "To jest metoda statyczna" obj = MyClass() print(obj.instance_method()) # Instancja: <MyClass object...> print(MyClass.class_method()) # Klasa: <class 'MyClass'> print(MyClass.static_method())# To jest metoda statyczna
Kluczowe cechy:
Czy można wywołać metodę instancji bezpośrednio przez klasę?
Tak, ale trzeba jawnie przekazać obiekt:
MyClass.instance_method(obj)
co rzadko jest stosowne.
Zachowanie metod statycznych przy dziedziczeniu: czy można je nadpisywać?
Tak, można zdefiniować staticmethod w klasie pochodnej o tej samej nazwie, i to ta metoda będzie wywoływana podczas odwołania z dziedziczenia.
Do czego służą metody klasowe, skoro zawsze można używać metod statycznych z cls jako argumentem?
cls — to nie tylko pierwszy argument: w classmethod Python sam wstawia odpowiednią klasę jako cls, nawet jeśli wywołanie pochodzi z klasy dziedziczącej. Pozwala to na tworzenie alternatywnej hierarchii konstruktorów bez sztywnego powiązania z rodzicem.
Przykład:
class Base: @classmethod def make(cls): return cls() class Child(Base): pass Child.make() # zwróci Child, a nie Base
W projekcie używano zwykłych metod do tworzenia alternatywnych instancji (np. create_from_json). Spowodowało to, że podczas dziedziczenia metoda zawsze zwracała obiekt klasy bazowej, a nie dziedzica.
Zalety:
Wady:
Zaimplementowano fabryki classmethod, które zwracają instancje bieżącej klasy (cls()), nawet jeśli są wywoływane z dziedzica.
Zalety:
Wady: