Próbuję zrozumieć, kiedy użyć __getattr__
lub __getattribute__
. Dokumentacja wspomina __getattribute__
dotyczy klas w nowym stylu. Co to są zajęcia w nowym stylu?
python
getattr
getattribute
Yarin
źródło
źródło
Odpowiedzi:
Kluczowa różnica między
__getattr__
i__getattribute__
polega na tym, że__getattr__
wywoływana jest tylko wtedy, gdy atrybut nie został znaleziony w zwykły sposób. Jest dobry do implementacji rezerwowania brakujących atrybutów i prawdopodobnie jest jednym z dwóch, których potrzebujesz.__getattribute__
jest wywoływany przed spojrzeniem na rzeczywiste atrybuty obiektu, więc może być trudny do prawidłowego wdrożenia. Bardzo łatwo możesz skończyć w nieskończonych rekurencjach.Klasy w nowym stylu pochodzą, klasy w
object
starym stylu są w Pythonie 2.x bez wyraźnej klasy bazowej. Ale rozróżnienie między klasami w starym i nowym stylu nie jest ważne przy wyborze między__getattr__
i__getattribute__
.Prawie na pewno chcesz
__getattr__
.źródło
__getattribute__
będą wezwani do każdego dostępu i__getattr__
będą wezwani do czasów, które__getattribute__
wzbudziłyAttributeError
. Dlaczego nie zatrzymać tego wszystkiego w jednym?__getattribute__
.objec.__getattribute__
wywołujemyclass.__getattr__
w odpowiednich okolicznościach.Zobaczmy kilka prostych przykładów obu
__getattr__
i__getattribute__
magicznych metod.__getattr__
Python wywoła
__getattr__
metodę za każdym razem, gdy zażądasz atrybutu, który nie został jeszcze zdefiniowany. W poniższym przykładzie moja klasa Count nie ma__getattr__
metody. Teraz w głównym przy próbie dostępu zarównoobj1.mymin
iobj1.mymax
atrybuty wszystko działa poprawnie. Ale kiedy próbuję uzyskać dostęp doobj1.mycurrent
atrybutu - Python daje miAttributeError: 'Count' object has no attribute 'mycurrent'
Teraz moja klasa Count ma
__getattr__
metodę. Teraz, gdy próbuję uzyskać dostęp doobj1.mycurrent
atrybutu - python zwraca mi wszystko, co zaimplementowałem w mojej__getattr__
metodzie. W moim przykładzie, ilekroć próbuję wywołać atrybut, który nie istnieje, Python tworzy ten atrybut i ustawia go na wartość całkowitą 0.__getattribute__
Teraz zobaczmy
__getattribute__
metodę. Jeśli masz__getattribute__
metodę w swojej klasie, Python wywołuje tę metodę dla każdego atrybutu, niezależnie od tego, czy istnieje. Dlaczego więc potrzebujemy__getattribute__
metody? Jednym z dobrych powodów jest to, że możesz uniemożliwić dostęp do atrybutów i zwiększyć ich bezpieczeństwo, jak pokazano w poniższym przykładzie.Ilekroć ktoś próbuje uzyskać dostęp do moich atrybutów, które zaczynają się od podłańcucha „cur”, python podnosi
AttributeError
wyjątek. W przeciwnym razie zwraca ten atrybut.Ważne: Aby uniknąć nieskończonej rekurencji w
__getattribute__
metodzie, jej implementacja powinna zawsze wywoływać metodę klasy podstawowej o tej samej nazwie, aby uzyskać dostęp do wszystkich potrzebnych atrybutów. Na przykład:object.__getattribute__(self, name)
lubsuper().__getattribute__(item)
nieself.__dict__[item]
WAŻNY
Jeśli twoja klasa zawiera zarówno magiczne metody getattr, jak i getattribute , wówczas
__getattribute__
jest wywoływana jako pierwsza. Ale jeśli__getattribute__
podniesieAttributeError
wyjątek, wyjątek zostanie zignorowany i__getattr__
zostanie wywołana metoda. Zobacz następujący przykład:źródło
__getattribute__
ale na pewno tak nie jest. Ponieważ w twoim przykładzie wszystko, co robisz,__getattribute__
to zgłaszanieAttributeError
wyjątku, jeśli atrybutu nie ma w__dict__
obiekcie; ale tak naprawdę nie jest to potrzebne, ponieważ jest to domyślna implementacja,__getattribute__
a tak naprawdę jest__getattr__
to dokładnie to, czego potrzebujesz jako mechanizmu awaryjnego.current
jest zdefiniowana na wystąpieńCount
(patrz__init__
), więc po prostu podnoszącAttributeError
jeżeli atrybut nie istnieje nie do końca, co się dzieje - to odracza się__getattr__
do wszystkich nazw rozpoczynając „CUR”, w tymcurrent
, ale takżecurious
,curly
...To tylko przykład oparty na wyjaśnieniu Neda Batcheldera .
__getattr__
przykład:A jeśli użyjesz tego samego przykładu
__getattribute__
, otrzymasz >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
źródło
__getattr__()
implementacje akceptują tylko skończony zestaw poprawnych nazw atrybutów, zbierającAttributeError
nieprawidłowe nazwy atrybutów, unikając w ten sposób subtelnych i trudnych do debugowania problemów . Ten przykład bezwarunkowo akceptuje wszystkie nazwy atrybutów jako prawidłowe - dziwne (i szczerze mówiąc podatne na błędy) niewłaściwe użycie__getattr__()
. Jeśli chcesz mieć „całkowitą kontrolę” nad tworzeniem atrybutów, jak w tym przykładzie,__getattribute__()
zamiast tego.defaultdict
.__getattr__
, że zostanie wywołany przed wyszukiwaniem w nadklasie. Jest to OK dla bezpośredniej podklasyobject
, ponieważ jedynymi metodami, na których naprawdę Ci zależy, są magiczne metody, które i tak ignorują instancję, ale w przypadku bardziej złożonej struktury dziedziczenia całkowicie usuwasz możliwość dziedziczenia czegokolwiek od rodzica.Klasy w nowym stylu dziedziczą
object
po innej klasie lub po niej:Klasy w starym stylu nie:
Dotyczy to tylko Pythona 2 - w Pythonie 3 wszystkie powyższe będą tworzyć klasy w nowym stylu.
Zobacz 9. Klasy (samouczek języka Python), NewClassVsClassicClass i Jaka jest różnica między starymi stylami a nowymi klasami stylów w Pythonie? dla szczegółów.
źródło
Klasy nowego stylu to te, które podklasują „obiekt” (bezpośrednio lub pośrednio). Mają
__new__
metodę klasową__init__
i mają nieco bardziej racjonalne zachowanie na niskim poziomie.Zwykle będziesz chciał przesłonić
__getattr__
(jeśli przesłonisz albo), w przeciwnym razie będziesz miał trudności z obsługą składni „self.foo” w ramach twoich metod.Dodatkowe informacje: http://www.devx.com/opensource/Article/31482/0/page/4
źródło
Czytając płytkę drukowaną Beazley & Jones, natknąłem się na konkretny i praktyczny przypadek użycia,
__getattr__
który pomaga odpowiedzieć na część „kiedy” pytania PO. Z książki:„Ta
__getattr__()
metoda przypomina rodzaj wyszukiwania całościowego. Jest to metoda wywoływana, jeśli kod próbuje uzyskać dostęp do atrybutu, który nie istnieje”. Wiemy to z powyższych odpowiedzi, ale w recepturze PCB 8.15 ta funkcja służy do implementacji wzorca projektu delegacji . Jeśli Obiekt A ma atrybut Obiekt B, który implementuje wiele metod, do których Obiekt A chce delegować, zamiast redefiniować wszystkie metody obiektu B w obiekcie A tylko w celu wywołania metod obiektu B, zdefiniuj__getattr__()
metodę w następujący sposób:gdzie _b jest nazwą atrybutu obiektu A, który jest obiektem B. Gdy metoda zdefiniowana na obiekcie B
__getattr__
zostanie wywołana na obiekcie A, metoda zostanie wywołana na końcu łańcucha wyszukiwania. Uczyniłoby to również kod czystszym, ponieważ nie masz zdefiniowanej listy metod tylko do delegowania do innego obiektu.źródło