2010-04-17 18 views
83

Ostatnio grałem z Pythonem, a jedno, co wydaje mi się nieco dziwne, to szerokie zastosowanie "magicznych metod", np. aby udostępnić swoją długość obiekt implementuje metodę def __len__(self), a następnie wywoływana jest podczas pisania len(obj).Dlaczego Python używa "magicznych metod"?

Zastanawiam się, dlaczego obiekty nie definiują po prostu metody len(self) i czy są wywoływane bezpośrednio jako element obiektu, np. obj.len()? Jestem pewien, że muszą istnieć dobre powody, dla których Python robi to tak, jak to robi, ale jako początkujący nie odkryłem jeszcze, czym są.

+4

myślę ogólny powód to) historyczny oraz b) coś takiego jak 'len()' lub 'odwrócone()' odnosi się do wielu typów obiektów , ale metoda taka jak 'append()' odnosi się tylko do sekwencji, itp. –

Odpowiedz

55

AFAIK, len jest wyjątkowy pod tym względem i ma historyczne korzenie.

Oto cytat from the FAQ:

Why does Python use methods for some functionality (e.g. list.index()) but functions for other (e.g. len(list))?

The major reason is history. Functions were used for those operations that were generic for a group of types and which were intended to work even for objects that didn’t have methods at all (e.g. tuples). It is also convenient to have a function that can readily be applied to an amorphous collection of objects when you use the functional features of Python (map(), apply() et al).

In fact, implementing len(), max(), min() as a built-in function is actually less code than implementing them as methods for each type. One can quibble about individual cases but it’s a part of Python, and it’s too late to make such fundamental changes now. The functions have to remain to avoid massive code breakage.

innych „metod magicznych” (faktycznie zwane specjalna metoda w folklorze Python) zrobić dużo rozsądku, i podobną funkcjonalność istnieje w innych językach. Są najczęściej używane do kodu, który jest wywoływany niejawnie, gdy używana jest specjalna składnia.

Na przykład:

  • przeciążone operatory (istnieje w C++ i inne)
  • konstruktora/destruktora
  • haki dla atrybutów dostępu
  • narzędzia dla metaprogramowanie

i tak dalej ...

+2

Och kochanie. Teraz czuję się głupio, zadając pytanie, na które udzielono odpowiedzi w FAQ. Chyba powinienem przeczytać resztę, więc nie pytam o nic innego, na co tam odpowiedziano: -S –

+12

@Greg: duża liczba upvotes i "ulubionych" gwiazd pokazuje, że wiele osób uznało to za interesujące, więc nie myśl, że powinieneś czuć się głupio :-) –

+1

[Python i zasada najmniejszego zdumienia] (http://lucumr.pocoo.org/2011/7/9/python-and-pola/) jest dobrze się zapoznaj z niektórymi zaletami tego Pythona (chociaż przyznaję, że angielski wymaga pracy). Podstawowy punkt: pozwala standardowej bibliotece wdrożyć tonę kodu, który staje się bardzo, bardzo wielokrotnego użytku, ale nadal overridable. – jpmc26

19

Z Zen Pythona:

In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.

Jest to jeden z powodów - z metod niestandardowych, programiści będą wolne, aby wybrać inną nazwę metody, jak getLength(), length(), getlength() lub w ogóle. Python wymusza ścisłe nazewnictwo, dzięki czemu można używać wspólnej funkcji len().

Wszystkie operacje, które są wspólne dla wielu typów obiektów, są wprowadzane do magicznych metod, takich jak __nonzero__, lub __repr__. Są jednak w większości opcjonalne.

Przeciążanie operatorów odbywa się również za pomocą metod magicznych (np. __le__), więc warto używać ich również do innych typowych operacji.

+0

To jest przekonujący argument. Bardziej satysfakcjonujące, że "Guido naprawdę nie wierzył w OO" ... (jak widziałem gdzie indziej). –

4

Nie są to tak naprawdę "imiona magiczne". To tylko interfejs, który obiekt musi zaimplementować, aby zapewnić daną usługę. W tym sensie nie są one bardziej magiczne niż jakakolwiek wcześniej zdefiniowana definicja interfejsu, którą trzeba ponownie zastosować.

7

Niektóre z tych funkcji wykonują więcej niż jedną metodę (bez abstrakcyjnych metod na nadklasie). Na przykład bool() działa trochę jak ten:

def bool(obj): 
    if hasattr(obj, '__nonzero__'): 
     return bool(obj.__nonzero__()) 
    elif hasattr(obj, '__len__'): 
     if obj.__len__(): 
      return True 
     else: 
      return False 
    return True 

Możesz być również 100% pewność, że bool() zawsze zwraca Prawda czy fałsz; jeśli polegasz na metodzie, nie możesz być całkowicie pewien, co otrzymasz.

Niektóre inne funkcje, które mają stosunkowo skomplikowanych wdrożeń (bardziej skomplikowane niż te podstawowe metody magiczne są prawdopodobnie) są iter() i cmp(), a wszystkie metody atrybut (getattr, setattr i delattr). Rzeczy takie jak int mają także dostęp do magicznych metod, gdy robią przymus (można wdrożyć __int__), ale wykonują podwójne zadanie jako typy. len(obj) jest w rzeczywistości jedynym przypadkiem, w którym nie wierzę, że jest inny od obj.__len__().

+2

Zamiast 'hasattr()' użyłbym 'try:'/'except AttributeError:' i zamiast 'if obj .__ len __(): return True else: return False' Chciałbym po prostu powiedzieć 'return obj .__ len __ ()> 0' ale to tylko rzeczy stylistyczne. –

+0

W pythonie 2.6 (co przy okazji 'bool (x)' odnosi się do 'x .__ nonzero __()'), twoja metoda nie zadziałałaby. instancje bool mają metodę '__nonzero __()', a twój kod wywoływałby sam siebie, gdy obiekt byłby boolem. Być może 'bool (obj .__ bool __())' powinno być traktowane w ten sam sposób, w jaki traktowałeś '__len__'? (Czy ten kod faktycznie działa dla Pythona 3?) – Ponkadoodle

+0

Kołowa natura bool() była nieco celowo absurdalna, aby odzwierciedlić szczególnie kołowy charakter definicji. Istnieje argument, że należy go po prostu uznać za prymitywny. –

0

Nie ma zbyt wiele do dodania do powyższych dwóch postów, ale wszystkie "magiczne" funkcje w ogóle nie są magiczne. Są częścią modułu _ builtins__, który jest niejawnie/automatycznie importowany po uruchomieniu interpretera. IE:

from __builtins__ import * 

dzieje się za każdym razem przed rozpoczęciem programu.

Zawsze uważałem, że byłoby to bardziej poprawne, gdyby Python zrobił to tylko dla powłoki interaktywnej, a wymagane skrypty do importowania różnych części z wbudowanych potrzebnych. Również prawdopodobnie różne funkcje obsługi byłyby dobre w powłokach a interaktywne. Tak czy inaczej, sprawdź wszystkie funkcje, i zobaczyć, jak to jest bez nich:

dir (__builtins__) 
... 
del __builtins__ 
1

Choć powodem jest głównie historyczne, istnieją pewne osobliwości w Pythona len które sprawiają, że korzystanie z funkcji zamiast metody odpowiedniej .

Niektóre operacje w Pythonie są implementowane jako metody, na przykład list.index i dict.append, podczas gdy inne są realizowane jako callables i magicznymi sposobami, na przykład str i iter i reversed. Obie grupy różnią się na tyle, że różne podejście jest uzasadnione:

  1. Są powszechne.
  2. str, int i przyjaciele są typami. Bardziej sensowne jest wywołanie konstruktora.
  3. Implementacja różni się od wywołania funkcji. Na przykład iter może wywołać __getitem__, jeśli __iter__ jest niedostępny i obsługuje dodatkowe argumenty, które nie mieszczą się w wywołaniu metody. Z tego samego powodu zmieniono it.next() na next(it) w najnowszych wersjach Pythona - ma to więcej sensu.
  4. Niektóre z nich są bliskimi krewnymi operatorów. Istnieje składnia do wywoływania __iter__ i __next__ - nazywa się to pętlą for. W celu zachowania spójności funkcja jest lepsza. I poprawia to dla niektórych optymalizacji.
  5. Niektóre funkcje są po prostu zbyt podobna do reszty w jakiś sposób - repr działa jak str robi. Posiadanie str(x) kontra x.repr() może być mylące.
  6. Niektóre z nich rzadko używają rzeczywistej metody implementacji, na przykład isinstance.
  7. Niektóre z nich są rzeczywistymi operatorami, getattr(x, 'a') to inny sposób robienia x.a i getattr ma wiele z wyżej wymienionych cech.

Ja osobiście nazywam pierwszą metodą grupową i drugą typu operatora. To nie jest bardzo dobre wyróżnienie, ale mam nadzieję, że jakoś to pomoże.

Po tym, len nie pasuje dokładnie do drugiej grupy. Jest bliżej operacji w pierwszej, z tą różnicą, że jest bardziej powszechny niż prawie każdy z nich. Ale jedyne, co robi, to wywołanie __len__ i jest bardzo zbliżone do L.index. Istnieją jednak pewne różnice. Na przykład: __len__ może zostać wywołany w celu implementacji innych funkcji, takich jak bool, jeśli metoda nazywała się len, można złamać bool(x) z niestandardową metodą len, która robi zupełnie inną rzecz.

Krótko mówiąc, masz zestaw bardzo typowych funkcji, które mogą zostać zaimplementowane przez klasy, za pośrednictwem specjalnej funkcji (która zwykle robi więcej niż implementacja, jak zrobiłby to operator), podczas budowy obiektu, i wszystkie mają wspólne cechy. Cała reszta to metoda. I len jest nieco wyjątkiem od tej reguły.

13

Python używa słowa: - "Magiczne metody" ponieważ te metody naprawdę wykonują magię dla Ciebie. Jedną z największych zalet stosowania magicznych metod Pythona jest to, że zapewniają one prosty sposób, aby obiekty zachowywały się jak wbudowane typy. Oznacza to, że można uniknąć brzydkich, sprzecznych z intuicją i niestandardowych sposobów wykonywania podstawowych operacji.

Rozważmy następujący przykład: -

dict1 = {1 : "ABC"} 
dict2 = {2 : "EFG"} 

dict1 + dict2 
Traceback (most recent call last): 
    File "python", line 1, in <module> 
TypeError: unsupported operand type(s) for +: 'dict' and 'dict' 

Daje to błąd, ponieważ typ słownika nie obsługuje dodawanie. A teraz rozszerzyć słownika klasę i dodać „__add__” magiczna metoda: -

class AddableDict(dict): 

    def __add__(self, otherObj): 
     self.update(otherObj) 
     return AddableDict(self) 


dict1 = AddableDict({1 : "ABC"}) 
dict2 = AddableDict({2 : "EFG"}) 

print (dict1 + dict2) 

teraz, daje następujące wyniki,

{1: 'ABC', 2: 'EFG'} 

Zatem dodając ten sposób, nagle magia stało i błąd, który otrzymujesz wcześniej, zniknął.

Mam nadzieję, że wszystko wyjaśni. Aby uzyskać więcej informacji, patrz link podany poniżej: -

http://web.archive.org/web/20161024123835/http://www.rafekettler.com/magicmethods.html

Powiązane problemy