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

Usunięta treść Dodana treść
zamiana ProstaNawigacja->Subst:ProstaNawigacja
Piotr (dyskusja | edycje)
trochę zmian
Linia 1:
{{Podświetl|py}}
== Pobieranie i ustawianie elementów ==
 
Oprócz normalnych metod, jest też kilka (może kilkanaście) metod specjalnych, które można definiować w klasach Pythona. Nie wywołujemy one wywoływaneich bezpośrednio z Twojegonaszego kodu (jak zwykłe metody). Wywołuje je za Ciebienas Python w określonych okolicznościach lub gdy użyjeszużyjemy określonej składni np. za pomocą metod specjalnych możemy nadpisać operację dodawania, czy też odejmowania.
 
JakZ zauważyłeśnormalnym wsłownikiem poprzednim rozdziale, użycie normalnych metod pozwalamożemy zrobić duży krok w stronę obudowania słownika klasą. Ale ponieważ możesz zrobić ze słownikiem dużo więcej, niż bezpośrednio wywołać jego metody,. normalneSame metody nie wystarczająwystarczą. MożeszMożemy na przykład [[../Słowniki|pobierać]] i dodawać[[../Słowniki|wstawiać]] elementy, używającdzięki wykorzystaniu odpowiedniej składni, niebez wywołującjawnego metodwywoływania jawniemetod. WMożemy tymtak przypadkurobić przydadządzięki sięmetodom metody specjalne, którespecjalnym. pozwalająPython zmapowaćodpowiednie elementy składni przekształca na odpowiednie wywołania metodfunkcji specjalnych.
 
{{Python/Przykład
'''Przykład |5.12 |Metoda <ttcode>__getitem__</ttcode>'''
|tekst=
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{{samp|{'nameplik':'/music/_singles/kairo.mp3'}}}
>>> f.__getitem__("nameplik") #(1)
{{samp|'/music/_singles/kairo.mp3'}}
>>> f["nameplik"] #(2)
{{samp|'/music/_singles/kairo.mp3'}}
 
# Metoda specjalna <code>__getitem__</code> wygląda dość prosto. Ta metoda specjalna pozwala słownikowi zwrócić pewną wartość na podstawie podanego klucza. A jak ta metodę możemy wywołać? Możemy to zrobić bezpośrednio, ale w praktyce nie robimy w ten sposób, byłoby to niezbyt wygodne. Najlepiej pozwolić Pythonowi wywołać tę metodę za nas.
def __getitem__(self, key): return self.data[key]
#Takiej Z takiej składni użyłbyśkorzystamy, by dostać pewną wartość ze słownika i dokładnie tę wartość dostajesz. AW brakującerzeczywistości ogniwoPython jestautomatycznie takie:przekształca wewnętrznie Python przekształcił tętaką składnię na wywołanie metody <ttcode>f.__getitem__("nameplik")</ttcode>. Właśnie dlatego <ttcode>__getitem__</ttcode> jest specjalnąnazywamy metodą specjalną: nie tylko możeszmożemy ją zawołać, ale Python woławywołuje tę metodę także za Ciebienas, gdykiedy skorzystamy użyjeszz odpowiedniej składni.
}}
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__getitem__("name") #(1)
'/music/_singles/kairo.mp3'
>>> f["name"] #(2)
'/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ć jego wartość. 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.
 
Oczywiście jest też specjalna metoda <tt>__setitem__</tt> komplementarna z <tt>__getitem__</tt>
 
Istnieje także analogiczna do <code>__getitem__</code> metoda <code>__setitem__</code>, która zamiast pobierać pewną wartość, zmienia daną wartość korzystając z pewnego klucza.
'''Przykład 5.13. Metoda <tt>__setitem__</tt>'''
 
{{Python/Przykład
def __setitem__(self, key, item): self.data[key] = item
'''Przykład |5.13. |Metoda <ttcode>__setitem__</ttcode>'''
|tekst=
>>> f
{{samp|{'nameplik':'/music/_singles/kairo.mp3'}}}
>>> f.__setitem__("genre", 31) #(1)
>>> f
{{samp|{'nameplik':'/music/_singles/kairo.mp3', 'genre':31}}}
>>> f["genregatunek"] = 32 #(2)
>>> f
{{samp|{'nameplik':'/music/_singles/kairo.mp3', 'genregatunek':32}}}
 
# TakAnalogicznie jakdo <ttcode>__getitem__</ttcode>, metodamożemy za pomocą <ttcode>__setitem__</ttcode> pozmienić prostuwartość pozwalapewnego metodzieklucza <tt>self.data</tt>znajdującego słownikasię wykonaćw jej pracęsłowniku. I takPodobnie, jak w przypadku <ttcode>__getitem__</ttcode> nie musisz wołaćmusimy jej wywoływać w ten sposób bezpośredni. Python wywoła <ttcode>__setitem__</ttcode>, jeśli tylko użyjeszużyjemy odpowiedniej składni.
# W taki praktyczny sposób korzystamy ze słownika. Za pomocą tej linii kodu Python wywołuje w sposób ukryty <code>f.__setitem__("gatunek", 32)</code>.
# Ta linia wygląda jak użycie słownika. Tylko, że <tt>f</tt> jest obiektem klasy próbującej udawać słownik a metoda <tt>__setitem__</tt> jest ważną częścią tej maskarady. Ta linia kodu powoduje właściwie wywołanie <tt>f.__setitem__("genre", 32)</tt>.
}}
 
<ttcode>__setitem__</ttcode> jest metodą specjalną, ponieważ Python wywołuje ją za Ciebienas, ale ciągle jest metodą klasy. TakKiedy łatwodefiniujemy klasy, jakmożemy zdefiniowałeśdefiniować pewne wmetody, klasienawet <tt>UserDict</tt>,jeśli możesznadklasa ma przedefiniowaćjuż wzdefiniowaną klasach pochodnychmetodą. iW zasłonićten metodęsposób klasynadpisujemy podstawowej(ang. To''override'') pozwalametody Cinadklas. definiować klasy, które czasami zachowująTyczy się jak zwykły słownik aleto potrafiątakże cośmetod więcejspecjalnych.
 
Koncepcja ta jest bazą całego szkieletu, który analizujemy w tym rozdziale. Każdy typ plików może posiadać własną klasę obsługi, która wie, w jaki sposób pobrać metadane z konkretnego typu plików. Natychmiast po poznaniu niektórych atrybutów (jak nazwa pliku i położenie), klasa obsługi będzie wiedziała, jak pobrać dalsze atrybutymetaatrybuty automatycznie. Możemy to zrobić poprzez nadpisanie metody <ttcode>__setitem__</ttcode> , sprawdzeniew której sprawdzamy poszczególnych kluczy i wykonanie dodatkowych operacji, jeśli dany klucz zostanie znaleziony, wykonujemy dodatkowe operacje.
 
Na przykład <ttcode>MP3FileInfo</ttcode> jest potomkiempodklasą <ttcode>FileInfo</ttcode>. Kiedy w <ttcode>MP3FileInfo</ttcode> ustawiamy klucz <ttcode>"nameplik"</ttcode>, nie tylko ustawiamy samwartość kluczsamego klucza <ttcode>"nameplik"</ttcode> (jak to robi jego przodek <tt>FileInfo</tt>słownik), lecz także zaglądamy do samego pliku, odczytujemy tagi MP3 i tworzymy pełny zbiór kluczy. Poniższy przykład pokazuje, w jaki sposób to działa.
 
{{Python/Przykład
'''Przykład |5.14. |Nadpisywanie metody <ttcode>__setitem__</ttcode> w klasie <ttcode>MP3FileInfo</ttcode>'''
def __setitem__(self, key, item): #(1)
|tekst=
if key == "name" and item: #(2)
<nowiki> def __setitem__(self, key, item): self.data[key] = item #(1)
def __setitem__(self, if key, == "plik" and item): #(12)
self.__parse(item) #(3)
FileInfo.__setitem__(self, key, item) #(4)</nowiki>
 
# Zauważmy,Zwróćmy żeuwagę na kolejność i liczbę argumentów w <ttcode>__setitem__</ttcode>. Pierwszym argumentem jest definiowanyinstancja wdanej identycznyklasy sposób(argument <code>self</code>), jakz wktórej ta metoda została wywołana, następnym argumentem jest klucz (argument <code>key</code>), który chcemy ustawić, a trzecim jest wartość (argument <code>item</code>), którą chcemy skojarzyć z klasiedanym przodkakluczem. JestKolejność tota ważnejest ważna, ponieważ Python będzie wywoływał tę metodą oczekującw odpowiedniejtakiej liczbykolejności i z taką liczbą argumentów.(Ściśle mówiąc, nazwy(Nazwy argumentów nic nie znaczą, ważna jest ich ilość i kolejność.)
# W tym miejscu zawarte jest sedno całej klasy <tt>MP3FileInfo</tt>: jeśli przypisujeszprzypisujemy pewną wartość do klucza <tt>"nameplik"</tt>, chceszchcemy wykonać dodatkowo pewne operacje.
# Dodatkowe operacje dla klucza <tt>"nameplik"</tt> zawarte są w metodzie <ttcode>__parse</ttcode>. Jest to inna metoda klasy <ttcode>MP3FileInfo</ttcode>. i kiedy jąKiedy wywołujemy, używamymetodę przy<code>__parse</code> tymużywamy zmiennej <ttcode>self</ttcode>. WywołującGdybyśmy wywołali samo <ttcode>__parse</ttcode>, odnosimyodnieślibyśmy się do normalnej funkcji, która jest zdefiniowana poza klasą, a tego nie chcemy wykonać. Kiedy natomiast wywołamy <ttcode>self.__parse</ttcode> 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ę przodkanadklasy. PamiętajPamiętajmy, toże Python nigdy nie będziezrobi zrobioneto za Ciebienas; wmusimy Pythonie: musiszzrobić to zrobić ręcznie. ZwróćZwróćmy uwagę na to, że odwołujemy się do bezpośredniegobezpośredniej przodkanadklasy, czyli do <ttcode>FileInfo</ttcode>, chociaż on nie posiada żadnej metody o nazwie <ttcode>__setitem__</ttcode>. Jednak wszystko jest w porządku, ponieważ Python będzie wędrował po drzewie 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ę <ttcode>__setitem__</ttcode>, zdefiniowanąktóra jest zdefiniowana w samej wbudowanej klasie słownika, w klasie <ttcode>UserDictdict</ttcode>.
 
{{Infobox|
Kiedy odwołujeszodwołujemy się do danych zawartych w atrybucie klasy, musiszmusimy określić nazwę atrybutu np. <ttcode>self.attribute</ttcode>. Podczas wywoływania metody klasy, musisz określić nazwę metody np. <ttcode>self.method</ttcode>.
}}
 
{{Python/Przykład
'''Przykład 5.15. Ustawianie <tt>"name"</tt> w <tt>MP3FileInfo</tt>'''
|5.15|Ustawianie klucza <code>"plik"</tt> w <code>MP3FileInfo</code>
 
|tekst=
>>> import fileinfo
>>> mp3file = fileinfo.MP3FileInfo() #(1)
>>> mp3file
{{samp|{'nameplik':None}}}
>>> mp3file["nameplik"] = "/music/_singles/kairo.mp3" #(2)
>>> mp3file
{{samp|<nowiki>{'album': 'Rave Mix', 'artistrok': '***DJ MARY-JANE***2000', 'genrekomentarz': 31'http://mp3.com/DJMARYJANE',
u'titletytu\u0142': 'KAIRO****THE BEST GOA', 'nameartysta': '/music/_singles/kairo.mp3***DJ MARY-JANE***',
'yeargatunek': '2000'31, 'commentplik': 'http:/music/_singles/kairo.mp3.com'}</DJMARYJANE'nowiki>}}
>>> mp3file["nameplik"] = "/music/_singles/sidewinder.mp3" #(3)
>>> mp3file
{{samp|<nowiki>{'album': '', 'artistrok': 'The Cynic Project2000', 'genrenazwa': 18, 'title': 'Sidewinder/music/_singles/sidewinder.mp3',
'namekomentarz': 'http:/music/_singlesmp3.com/sidewinder.mp3cynicproject', u'yeartytu\u0142': '2000Sidewinder',
'artysta': 'The Cynic Project', 'gatunek': 18}</nowiki>}}
'comment': 'http://mp3.com/cynicproject'}
 
# Najpierw tworzysztworzymy instancję klasy <ttcode>MP3FileInfo</ttcode> bez podawania nazwy pliku. (Możemy tak zrobić, ponieważ argument <ttcode>filename</ttcode> funkcjimetody <ttcode>__init__</ttcode> jest opcjonalny.) Ponieważ <ttcode>MP3FileInfo</ttcode> nie posiada własnej metody <ttcode>__init__</ttcode>, Python idzie wyżej po drzewie przodkównadklas i znajduje metodę <ttcode>__init__</ttcode> w klasie <ttcode>FileInfo</ttcode>. Z kolei <ttcode>__init__</ttcode> w tej klasie ręcznie wykonuje metodę <ttcode>__init__</ttcode> w klasie <ttcode>UserDictdict</ttcode>, a potem ustawia klucz <ttcode>"nameplik"</ttcode> na <ttcode>filename</ttcode>, który wynosi <ttcode>None</ttcode>, ponieważ pominęliśmy nazwę pliku. Ostatecznie <ttcode>mp3file</ttcode> początkowo przypominajest słowniksłownikiem (właściwie podklasą słownika) z jednym kluczem <ttcode>"nameplik"</ttcode>, którego wartość wynosi <tt>None</tt>.
# Teraz rozpoczyna się prawdziwa zabawa. Ustawiając klucz <ttcode>"nameplik"</ttcode> w <ttcode>mp3file</ttcode> powoduje wywołanie metody <ttcode>__setitem__</ttcode> klasy <ttcode>MP3FileInfo</ttcode> (a nie słownika, czyli klasy <ttcode>UserDictdict</ttcode>),. któraZ kolei metoda ta zauważa, że ustawiamy klucz <ttcode>"nameplik"</ttcode> z prawdziwą wartością (<code>item</code> jest prawdą w kontekście logicznym) i wywołuje <ttcode>self.__parse</ttcode>. Chociaż jeszcze nie śledziliśmyanalizowaliśmy działania metody <ttcode>__parse</ttcode>, możemy po wyjściu zobaczyć, że ustawia ona kilka innych kluczy jak <ttcode>"album"</ttcode>, <tt>"artistartysta"</tt>, <ttcode>"genre"</ttcode>, <ttcode>u"titletytuł"</ttcode> (w unikodzie, bo korzystamy z polskich znaków), <ttcode>"yearrok"</ttcode>, czy też <ttcode>"comment"</ttcode>.
# ZmieniającKiedy zmienimy klucz <ttcode>"nameplik"</ttcode>, będziemy wykonywaliproces ten samzostanie proceswykonany podobnieponownie. Python wywoła <ttcode>__setitem__</ttcode>, który następnie wywoła <ttcode>self.__parse</ttcode>, a ten ustawi wszystkie inne klucze.
}}
 
<noinclude>
{{Nawigacja|Python|
[[../Tworzenie instancji klasy/]]|
[[Python/Klasa opakowująca UserDict|Klasa opakowująca <tt>UserDict</tt>]]|
[[../Atrybuty klas/]]|
[[Python/Zaawansowane specjalne metody|Zaawansowane specjalne metody]]|
}}
{{Podświetl|py}}
</noinclude>