Zanurkuj w Pythonie/Metody specjalne: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Nie podano opisu zmian
Robwolfe (dyskusja | edycje)
Nie podano opisu zmian
Linia 4:
Oprócz normalnych metod, jest też kilka metod specjalnych, które można definiować w klasach Pythona. Nie są one wywoływane bezpośrednio z Twojego kodu (jak zwykłe metody). Wywołuje je za Ciebie Python w określonych okolicznościach lub gdy użyjesz określonej składni.
 
Jak zauważyłeś w poprzednim rozdziale, użycie normalnych metod pozwala zrobić duży krok w stronę obudowania słownika klasą. Ale ponieważ możesz zrobić ze słownikiem dużo więcej, niż wywołać jego metody, normalne metody nie wystarczają. Możesz na przykład pobierać i dodawać elementy, używając składni nie wywołując metod jawnie. W tym przypadku przydadzą się klasymetody specjalne, które pozwalają zmapować elementy składni na wywołania metod.
 
'''Przykład 5.12 Metoda <tt>__getitem__</tt>'''
Linia 18:
'/music/_singles/kairo.mp3'
 
# Metoda specjalna <tt>__getitem__</tt> wygląda dość prosto. Tak jak zwykłe metody <tt>clear</tt> <tt>keys</tt> i <tt>values</tt> po prostu pozwala słownikowi zwrócić swojejego wartościwartość. Jak jest wywoływana? Możesz wywołać ją bezpośrednio, ale w praktyce tak się nie robi. Chciałem Ci tylko pokazać, jak to działa. Dobrym sposobem jest pozwolenie Pythonowi, żeby wywołał ją za Ciebie.
#Takiej składni użyłbyś, by dostać wartość ze słownika i dokładnie tę wartość dostajesz. A brakujące ogniwo jest takie: wewnętrznie Python przekształcił tę składnię na wywołanie metody <tt>f.__getitem__("name")</tt>. Właśnie dlatego <tt>__getitem__</tt> jest specjalną metodą: nie tylko możesz ją zawołać ale Python woła ją za Ciebie, gdy użyjesz odpowiedniej składni.
 
Linia 41:
<tt>__setitem__</tt> jest metodą specjalną, ponieważ Python wywołuje ją za Ciebie, ale ciągle jest metodą klasy. Tak łatwo, jak zdefiniowałeś ją w klasie <tt>UserDict</tt>, możesz ją przedefiniować w klasach pochodnych i zasłonić metodę klasy podstawowej. To pozwala Ci definiować klasy, które czasami zachowują się jak zwykły słownik ale potrafią coś więcej.
 
Koncepcja ta jest prostotąbazą całego szkieletu, który analizujemy w tym rozdziale. Każdy pliktyp plików może posiadać własną klasę obsługi, która wie, w jaki sposób pobrać danemetadane z różnychkonkretnego typówtypu plików. Natychmiast po pobraniupoznaniu niektórych atrybutów (jak nazwa pliku i lokacjapołożenie), które znamy, klasa obsługi będzie wiedziała, jak otrzymaćpobrać dalsze atrybuty automatycznie. Możemy to zrobić poprzez nadpisanie metody <tt>__setitem__</tt> , sprawdzenie poszczególnych kluczy i wykonywaniewykonanie dodatkowych operacji, jeśli dany klucz zostanie znaleziony.
 
Na przykład <tt>MP3FileInfo</tt> jest potomkiem <tt>FileInfo</tt>. Kiedy w <tt>MP3FileInfo</tt> ustawiamy klucz <tt>"name"</tt>, nie tylko ustawiamy sam klucz <tt>"name"</tt> (jak to robi jego przodek <tt>FileInfo</tt>), lecz także patrzymyzaglądamy do samego pliku, naodczytujemy tagi MP3 i dodajemytworzymy pełny zbiór kluczy. Poniższy przykład pokazuje, w jaki sposób to działa.
 
'''Przykład 5.14. Nadpisywanie metody <tt>__setitem__</tt> w klasie <tt>MP3FileInfo</tt>'''
Linia 52:
 
# Zauważmy, że <tt>__setitem__</tt> jest definiowany w identyczny sposób, jak w klasie przodka. Jest to ważne, ponieważ Python będzie wywoływał tę metodą oczekując odpowiedniej liczby argumentów.(Ściśle mówiąc, nazwy argumentów nic nie znaczą, ważna jest ich ilość.)
# W tym miejscu jest klucz klasysedno całej klasy <tt>MP3FileInfo</tt>: jeśli przypisujesz pewną wartość do klucza <tt>"name"</tt>, chcesz wykonać dodatkowo pewne operacje.
# Dodatkowe operacje dla klucza <tt>"name"</tt> zawarte są w metodzie <tt>__parse</tt>. Jest to inna metoda klasy <tt>MP3FileInfo</tt> i kiedy ją wywołujemy, używamy przy tym zmiennej <tt>self</tt>. Wywołując samo <tt>__parse</tt> odnosimy się do normalnej funkcji, która jest zdefiniowana poza klasą, a tego nie chcemy wykonać. Kiedy natomiast wywołamy <tt>self.__parse</tt> będziemy odnosić się do metody znajdującej się wewnątrz klasy. Nie jest to niczym nowym. W identyczny sposób odnosimy się do atrybutów klasy.
# Po wykonaniu tej dodatkowej operacji, chcemy wykonać metodę przodka. ZapamiętajPamiętaj, to co nigdy nie będzie dlazrobione ciebieza zrobioneCiebie w Pythonie: musisz to zrobić manualnieręcznie. Zwróć uwagę na to, że odwołujemy się bezpośredniodo bezpośredniego przodka, czyli <tt>FileInfo</tt>, chociaż on nie posiada żadnej metody o nazwie <tt>__setitem__</tt>. Jednak wszystko jest w porządku, ponieważ Python będzie szedłwędrował po przodkachdrzewie przodków jeszcze wyżej dopóki nie znajdzie klasy, która posiada metodę, którą wywołujemy. Tak więc ta linia kodu znajdzie i wywoła metodę <tt>__setitem__</tt> zdefiniowaną w klasie <tt>UserDict</tt>.
 
{{Infobox|
Linia 77:
'comment': 'http://mp3.com/cynicproject'}
 
# Najpierw tworzysz instancję klasy <tt>MP3FileInfo</tt> pomijającbez nazwępodawania nazwy pliku. (Możemy tak zrobić, ponieważ argument <tt>filename</tt> funkcji <tt>__init__</tt> jest opcjonalny.) Ponieważ <tt>MP3FileInfo</tt> nie posiada własnej metody <tt>__init__</tt>, Python idzie wyżej po drzewie przodków i znajduje metodę <tt>__init__</tt> w klasie <tt>FileInfo</tt>. Z kolei <tt>__init__</tt> w tej klasie manualnieręcznie wykonuje metodę <tt>__init__</tt> w klasie <tt>UserDict</tt>, a potem ustawia klucz <tt>"name"</tt> na <tt>filename</tt>, który wynosi <tt>None</tt>, ponieważ pominęliśmy nazwę pliku. Ostatecznie <tt>mp3file</tt> początkowo przypomina słownik z jednym kluczem <tt>"name"</tt>, którego wartość wynosi <tt>None</tt>.
# Teraz rozpoczyna się prawdziwa zabawa. Ustawiając klucz <tt>"name"</tt> w <tt>mp3file</tt> powoduje wywołanie metody <tt>__setitem__</tt> klasy <tt>MP3FileInfo</tt> (nie <tt>UserDict</tt>), która zauważa, że ustawiamy klucz <tt>"name"</tt> z prawdziwą wartością i wywołuje <tt>self.__parse</tt>. Chociaż jeszcze nie śledziliśmy działania metody <tt>__parse</tt>, możemy po wyjściu zobaczyć, że ustawia ona kilka innych kluczy jak <tt>"album"</tt>, <tt>"artist"</tt>, <tt>"genre"</tt>, <tt>"title"</tt>, <tt>"year"</tt>, czy też <tt>"comment"</tt>.
# Zmieniając klucz <tt>"name"</tt> będziemy wykonywali ten sam proces podobnie. Python wywoła <tt>__setitem__</tt>, który następnie wywoła <tt>self.__parse</tt>, a ten ustawi wszystkie inne klucze.