Czym są magiczne metody w Pythonie i jak ich używać

Wśród rozlicznych możliwości, jakie oferuje Python, na szczególną uwagę zasługuje implementacja tzw. magicznych metod. Wykorzystując je, możemy tworzyć kod bardziej przejrzysty, intuicyjny i łatwy do zrozumienia.

Te wyjątkowe metody pozwalają na tworzenie interfejsów umożliwiających interakcję z obiektami w sposób charakterystyczny dla Pythona. Ten artykuł zapozna Cię z koncepcją magicznych metod, przedstawi dobre praktyki związane z ich tworzeniem i omówi najczęściej spotykane z nich.

Czym są magiczne metody?

Magiczne metody w Pythonie to specjalne funkcje, które określają, jak obiekty zachowują się podczas wykonywania na nich typowych operacji. Charakteryzują się one tym, że ich nazwy są otoczone podwójnymi podkreślnikami z przodu i z tyłu.

Dzięki tej unikalnej konwencji nazewniczej często nazywane są metodami „dunder” (od double underscore). Przykładem takiej metody, z którą prawdopodobnie już się spotkałeś, jest __init__(), wykorzystywana do definiowania konstruktorów klas.

Zazwyczaj metody dunder nie są wywoływane bezpośrednio w kodzie. Są one raczej aktywowane przez interpreter Pythona podczas wykonywania programu.

Dlaczego metody magiczne są przydatne?

Magiczne metody są fundamentalnym elementem programowania obiektowego w Pythonie. Pozwalają one na definiowanie zachowania niestandardowych typów danych podczas ich używania z typowymi, wbudowanymi operacjami. Te operacje obejmują:

🟢 Operacje arytmetyczne

🟢 Operacje porównania

🟢 Operacje związane z cyklem życia obiektu

🟢 Operacje reprezentacji obiektu

W dalszej części tekstu wyjaśnimy, jak zaimplementować magiczne metody, które definiują zachowanie aplikacji w kontekście każdej z wymienionych kategorii.

Jak zdefiniować magiczne metody

Jak już wspomniano, magiczne metody determinują zachowanie obiektów, dlatego definiuje się je jako część klasy obiektu. Jako składnik klasy, przyjmują one jako pierwszy argument self, który odnosi się do samego obiektu.

W zależności od kontekstu, w jakim są wywoływane przez interpreter, mogą przyjmować dodatkowe argumenty. Charakterystyczne dla nich jest również użycie dwóch podkreślników przed i po nazwie.

Realizacja

Dotychczasowe rozważania mogą wydawać się dość abstrakcyjne. W tej sekcji przejdziemy do praktyki, implementując prostą klasę Rectangle (Prostokąt).

Klasa ta będzie posiadała atrybuty długości i szerokości, które można określić podczas tworzenia jej instancji za pomocą metody __init__. Dodatkowo, będziemy mogli porównywać różne prostokąty, sprawdzając, czy są one równe, mniejsze lub większe od siebie, wykorzystując operatory ==, < i >. Na koniec, zadbamy o to, by prostokąt dostarczał sensowną reprezentację w postaci łańcucha znaków.

Konfigurowanie środowiska kodowania

Aby móc śledzić ten poradnik, potrzebujesz środowiska Python. Możesz wykorzystać lokalne środowisko lub internetowy kompilator Pythona, np. dostępny na newsblog.pl.

Tworzenie klasy Rectangle

Zacznijmy od zdefiniowania klasy Rectangle.

class Rectangle:
    pass

Tworzenie metody konstruktora

Teraz utworzymy naszą pierwszą magiczną metodę – konstruktor klasy. Będzie ona przyjmować wysokość i szerokość, a następnie zapisywać je jako atrybuty instancji klasy.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

Tworzenie magicznej metody reprezentacji łańcuchów

Następnie chcemy stworzyć metodę, dzięki której nasza klasa będzie generować zrozumiały dla człowieka ciąg znaków reprezentujący obiekt. Ta metoda zostanie wywołana przy każdym użyciu funkcji str() z instancją klasy Rectangle jako argumentem. Będzie ona również używana w sytuacjach, gdy oczekiwany jest argument typu łańcuch znaków, np. w funkcji print.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def __str__(self):
        return f'Rectangle({self.height}, {self.width})'

Metoda __str__() powinna zwracać ciąg znaków reprezentujący obiekt. W tym przypadku zwracamy format Rectangle(, ), gdzie wysokość i szerokość to wymiary prostokąta.

Tworzenie magicznych metod dla operacji porównawczych

Teraz zajmiemy się tworzeniem operatorów porównania dla operacji równości, mniejszości i większości. Ten proces nazywa się przeciążaniem operatorów. W tym celu użyjemy magicznych metod __eq__, __lt__ i __gt__. Metody te zwrócą wartość logiczną po porównaniu powierzchni prostokątów.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def __str__(self):
        return f'Rectangle({self.height}, {self.width})'

    def __eq__(self, other):
        """ Sprawdzanie równości """
        return self.height * self.width == other.height * other.width

    def __lt__(self, other):
        """ Sprawdzanie, czy prostokąt jest mniejszy od drugiego """
        return self.height * self.width < other.height * other.width

    def __gt__(self, other):
        """ Sprawdzanie, czy prostokąt jest większy od drugiego """
        return self.height * self.width > other.height * other.width

Jak widać, te metody przyjmują dwa parametry. Pierwszy to bieżący prostokąt, a drugi to wartość, z którą jest on porównywany. Ta wartość może być inną instancją Rectangle lub dowolną inną wartością. Logika porównania oraz warunki, w których porównanie zwraca wartość logiczną, zależą wyłącznie od Ciebie.

Wspólne metody magiczne

W kolejnej sekcji omówimy najczęściej spotykane magiczne metody, które prawdopodobnie będziesz wykorzystywać.

# 1. Działania arytmetyczne

Magiczne metody arytmetyczne są wywoływane, gdy instancja klasy znajduje się po lewej stronie operatora arytmetycznego. Metoda jest wywoływana z dwoma argumentami: pierwszym jest odniesienie do instancji, a drugim obiekt po prawej stronie operatora. Poniżej przedstawiono metody i odpowiadające im operatory:

NameMethodSignDescriptionAddition__add__+Implementuje dodawanie.Subtraction__sub__–Implementuje odejmowanie.Multiplication__mul__*Implementuje mnożenie.Division__div__/Implementuje dzielenie.Floor distribution__floordiv__//Implementuje dzielenie całkowite.

#2. Operacje porównania

Podobnie jak w przypadku magicznych metod arytmetycznych, metody porównania są wywoływane, gdy instancja klasy, dla której zostały zdefiniowane, znajduje się po lewej stronie operatora porównania. Podobnie, są one wywoływane z dwoma parametrami: odniesieniem do instancji obiektu oraz odniesieniem do wartości po prawej stronie znaku.

NameMethodSignDescriptionLess than__lt__<Implementuje porównanie mniejszości.Greater than__gt__>Implementuje porównanie większości.Equal to__eq__==Implementuje porównanie równości.Less than or equal to__le__<=Implementuje porównanie mniejszości lub równości.Greater than or equal to__ge__>=Implementuje porównanie większości lub równości.

#3. Operacje cyklu życia

Metody te są wywoływane w odpowiedzi na różne etapy cyklu życia obiektu, takie jak jego tworzenie lub usuwanie. Konstruktor __init__ należy do tej kategorii. Poniższa tabela zawiera najczęściej używane metody z tej grupy:

NameMethodDescriptionConstructor__init__Metoda wywoływana podczas tworzenia instancji obiektu.Destructor__del__Ta metoda jest wywoływana przy usuwaniu obiektu. Można jej użyć do wykonania czynności porządkowych, jak zamknięcie otwartych plików.New__new__Metoda __new__ wywoływana jest jako pierwsza przy tworzeniu obiektu danej klasy. Jest ona wywoływana przed konstruktorem i przyjmuje klasę oraz ewentualne dodatkowe argumenty. Zwraca instancję klasy. W większości przypadków nie jest ona specjalnie użyteczna, ale warto o niej wiedzieć.

#4. Operacje reprezentacji

NameMethodDescriptionStr__str__Zwraca czytelny dla człowieka ciąg znaków reprezentujący obiekt. Metoda jest wywoływana przy użyciu funkcji str() lub print(). Powinna dostarczać reprezentację zrozumiałą dla użytkownika końcowego aplikacji.Repr__repr__Zwraca ciąg reprezentujący obiekt używany przez programistę. Zwracany łańcuch znaków powinien zawierać wystarczająco dużo informacji, aby na jego podstawie można było zbudować identyczną instancję obiektu.

Najlepsze praktyki tworzenia magicznych metod

Magiczne metody są bardzo przydatne, ale przy ich wykorzystaniu należy pamiętać o kilku zasadach.

  • Stosuj je z umiarem – nadmierne używanie magicznych metod może sprawić, że kod stanie się trudniejszy do zrozumienia. Ogranicz się do implementowania tylko tych naprawdę potrzebnych.
  • Upewnij się, że rozumiesz wpływ metod takich jak __setattr__ i __getattr__ na wydajność programu, zanim zaczniesz ich używać.
  • Dokumentuj zachowanie swoich magicznych metod, aby inni programiści wiedzieli, jak one działają i mogli je debugować w razie potrzeby.

Ostatnie słowa

W tym artykule zaprezentowano metody magiczne jako narzędzie do tworzenia klas, które mogą być używane z wbudowanymi operacjami. Omówiono również sposób ich definiowania, przedstawiono przykład klasy wykorzystującej magiczne metody i wymieniono najpopularniejsze z nich, a także zaprezentowano kilka dobrych praktyk.

W kolejnym kroku możesz zgłębić wiedzę na temat implementacji klasy Counter w Pythonie.


newsblog.pl