2015-12-28 11 views
8

W contextlib.py widzę, że klasa ExitStack wywołuje metodę __enter__() poprzez obiekt typu (type(cm)) zamiast bezpośrednich wywołań metod do danego obiektu (cm).Dlaczego `type (x) .__ wpisz __ (x)` zamiast `x .__ wpisz __()` w standardowym kontekście Python?

Zastanawiam się, dlaczego i dlaczego nie.

np

  • to daje lepsze ślady wyjątków, gdy wystąpi błąd
  • czy jest to specyficzne dla stylu kodowania jakiegoś modułu?
  • ma jakieś korzyści wydajności?
  • czy unika niektórych artefaktów/efektów ubocznych ze skomplikowanymi hierarchiami typów?

Odpowiedz

12

Przede wszystkim, to co się dzieje, gdy robisz with something, to nie tylko contextlib że patrzy specjalnej metody typu. Warto również zauważyć, że to samo dzieje się z innymi metodami specjalnymi: np. a + b wyniki w type(a).__add__(a, b).

Ale dlaczego tak się dzieje? Jest to pytanie często uruchamiane na listach dyskusyjnych python-dev i python-ideas. A kiedy mówię "często", mam na myśli "bardzo często".

Ostatni z nich to: Missing Core Feature: + - */| & do not call getattr i Eliminating special method lookup.

Oto kilka interesujących punktów:

Obecne zachowanie jest zgodne z projektem - specjalne metody są wzrok, gdy gniazdach na klasę obiektu, a nie jako atrybuty instancji. Umożliwia to interpreterowi pominięcie kilku kroków w procesie wyszukiwania atrybutów o normalnej instancji .

(Source)

Warto zauważyć, że zachowanie jest jeszcze bardziej magiczne niż to. Nawet podczas wyszukiwania w klasie domyślne wyszukiwanie metody omija __getattr__ i __getattribute__ metaklasy. Tak więc wyszukiwanie specjalne w trybie specjalnym nie jest zwykłym wyszukiwaniem, które dzieje się w klasie w klasie zamiast w instancji; jest to w pełni magiczne poszukiwanie , które nie angażuje zwykłych haków dostosowywania dostępu do atrybutu na żadnym poziomie na poziomie .

(Source)

Takie zachowanie jest również udokumentowane w dokumentacji referencyjnego: Special method lookup, który mówi:

Omijanie maszyny w ten sposób __getattribute__() zapewnia znaczące możliwości dla optymalizacji prędkości w tłumacza, w koszt pewnej elastyczności w posługiwaniu się specjalnymi metodami (specjalna metoda musi być ustawiona na samym obiekcie klasy, aby być konsekwentnie wywoływanym przez tłumacza).

W skrócie, wydajność jest głównym problemem. Ale przyjrzyjmy się temu bliżej.

Jaka jest różnica między type(obj).__enter__() a obj.__enter__()?

Po napisaniu obj.attr zostaje wywołany type(obj).__getattribute__('attr'). Domyślna implementacja __getattribute__() wyszukuje attr w słowniku instancji (tj. obj.__dict__) i w przestrzeni nazw klasy, a w przeciwnym razie wywołuje type(obj).__getattr__('attr').

To było szybkie wyjaśnienie i pominąłem niektóre szczegóły, jednak powinno ci dać wyobrażenie o tym, jak skomplikowane może być wyszukiwanie atrybutów i jak wolno to może się stać. Zwarcie w specjalnym wyszukiwaniu metod z pewnością zapewnia poprawę wydajności, ponieważ wyszukiwanie w stylu "klasycznym" może być zbyt wolne.

+0

Dzięki za odpowiedź. Zastanawiam się jednak, dlaczego 'type (a) .__ dodaj __ (a, b)' zamiast 'a .__ dodaj __ (b)'. Czyż nie są one takie same? – Achimnol

+1

@Achimnol: Nie są one takie same, jeśli przypisujesz coś do 'a .__ add__'. – user2357112

+1

@Achimnol: Wydaje mi się, że również odpowiedziałem na to pytanie, ale proszę, powiedz mi, czy byłem zbyt niejasny –