Twórz bez wysiłku klasy w Pythonie za pomocą @dataclass

Jan 22 2023
W Pythonie dekorator @dataclass upraszcza proces tworzenia klas, automatycznie dodając wspólne metody i promując najlepsze praktyki pisania dobrze napisanego i bezpiecznego kodu. Jest dostępny od Pythona 3.
Obraz wygenerowany przez Jacoba Ferusa

W Pythonie @dataclassdekorator upraszcza proces tworzenia klas, automatycznie dodając wspólne metody i promując najlepsze praktyki pisania dobrze napisanego i bezpiecznego kodu. Jest dostępny od Pythona 3.7. W tym artykule opowiem o tym dekoratorze.

Zacznijmy od prostego przykładu:

Definiując dwie zmienne statyczne ze wskazówkami typu i dodając dekorator @dataclass, otrzymujemy klasę z __init__metodą odpowiadającą następującemu:

Chociaż pomijanie __init__metody - jest przyjemne, klasa danych oferuje znacznie więcej niż tylko to. Aby to zrozumieć, spójrzmy na opcje dostępne dla dekoratora:

Pominąłem niektóre argumenty i pozostawiłem te, które uważam za najważniejsze do przedyskutowania. Pamiętaj, że używając:

równoważnie spowoduje użycie domyślnych argumentów.

Jak widać, w zależności od argumentów, metody zostaną dodane do klasy niejawnie. Domyślnie:

  • init=True doda, __init__jak widzieliśmy powyżej
  • repr=True doda __repr__, czyli to, co jest pokazane podczas drukowania
  • eq=True doda __eq__na podstawie pól
  • order=True doda __lt__(), __le__(), __gt__(), i__ge__()

Zauważ, że możesz oczywiście zdefiniować inne niestandardowe metody klasowe, tak jak zwykle w klasie. Poniżej definiuję prostą klasę bez dekoratora @dataclass dla porównania.

Druk

Z repr=True, które jest ustawione domyślnie, dane wyjściowe podczas drukowania są zmieniane. Porównajmy, co obiekt utworzony z klasą danych i obiekt utworzony z normalną klasą (bez __repr__) pokazuje się po wydrukowaniu:

Jak widać w przypadku normalnej klasy, wydrukowane informacje domyślnie mówią nam bardzo niewiele o obiekcie. Z drugiej strony dataclass pokazuje zwięźle wartości obiektu, zastępując domyślną __repr__metodę -.

Równość

Domyślne zachowanie równości dla klas niestandardowych może być mylące. Jeśli porównasz niestandardową klasę bez zdefiniowanej __eq__metody, dwa obiekty będą równe tylko wtedy, gdy będą dokładnie tym samym obiektem:

Zatem wartości atrybutów nie mają znaczenia. Ale za pomocą klasy danych i eq=True(ustawionej domyślnie) dodawana jest bardziej oczekiwana forma równości, w której porównywane są atrybuty (tj. __eq__jest zastępowana):

Zamówienie

Ustawiając orderparametr na true (domyślnie false) i eq true ( other=Truei eq=Falsespowoduje to wyrzucenie błędu), metody:

  • __lt__()
  • __le__()
  • __gt__()
  • __ge__()

Niezmienność i mieszanie

Na koniec porozmawiajmy o argumentach frozeni unsafe_hash. Argumenty te są bardzo przydatne i ułatwiają prawidłowe zarządzanie mieszaniem, równością i mutacjami. Jeśli nie jesteś zaznajomiony z haszowaniem w Pythonie, polecam przeczytanie artykułu: Understanding Hashing and Equality in Python with __hash__ and __eq__ .

Ustawienie frozen=Trueokreśla, że ​​klasa powinna być tylko do odczytu lub niezmienna, tj. po zainicjowaniu obiektu nie można zmienić żadnych pól:

Te dwie właściwości frozeni eqwpłyną na utworzoną niejawną metodę mieszania:

  • frozen=True, eq=True=> __hash__-metoda jest generowana automatycznie przy użyciu pól
  • frozen=True, eq=False=> domyślna __hash__-metoda, tzn. nieużywanie pól
  • frozen=False, eq=True=> Unhashable, błąd jest zgłaszany, jeśli jest hashowany
  • frozen=False, eq=False=> domyślna __hash__-metoda, tzn. nieużywanie pól

Ale dlaczego klasa musi być zamrożona? Jeśli dodasz obiekt do zestawu lub słownika, wygenerowany skrót jest używany do umieszczenia go w mapie skrótów. Później, gdy chcesz odzyskać obiekt, użyj tego samego skrótu, aby go znaleźć. Teraz, jeśli obiekt został zmieniony, hash również powinien się zmienić (ponieważ jest oparty na polach). W rezultacie nie będziesz mógł ponownie znaleźć obiektu. Tak więc, aby hash pozostał stały, pola, na których jest oparty, muszą pozostać stałe. Z tego powodu if eq=Truei frozen=False, nie ma dostępnej metody mieszania.

co z unsafe_hash? Jak opisano, predefiniowane zachowanie niejawnej definicji __hash__opiera się na zasadach promujących najlepsze praktyki. Jednakże, jeśli mimo to nadal chcesz mieć __hash__zdefiniowaną metodę, być może ze względu na szczególne okoliczności, możesz unsafe_hash=Trueją ustawić i tak się stanie. Należy jednak pamiętać, że niebezpieczne słowo kluczowe pojawia się nie bez powodu:

Wniosek

@dataclass w Pythonie to przydatny dekorator, który pozwala zaoszczędzić czas, postępować zgodnie z najlepszymi praktykami i uprościć proces tworzenia klas. Uważam, że nadaje się do wielu różnych sytuacji.

Dziękuje za przeczytanie!

Możesz być zainteresowanym także tym: