Zanurkuj w Pythonie/Metody specjalne
Pobieranie i ustawianie elementów
[edytuj]Oprócz normalnych metod, jest też kilka (może kilkanaście) metod specjalnych, które można definiować w klasach Pythona. Nie wywołujemy ich bezpośrednio z naszego kodu (jak zwykłe metody). Wywołuje je za nas Python w określonych okolicznościach lub gdy użyjemy określonej składni np. za pomocą metod specjalnych możemy nadpisać operację dodawania, czy też odejmowania.
Z normalnym słownikiem możemy zrobić dużo więcej, niż bezpośrednio wywołać jego metody. Same metody nie wystarczą. Możemy na przykład pobierać i wstawiać elementy dzięki wykorzystaniu odpowiedniej składni, bez jawnego wywoływania metod. Możemy tak robić dzięki metodom specjalnym. Python odpowiednie elementy składni przekształca na odpowiednie wywołania funkcji specjalnych.
__getitem__
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'plik':'/music/_singles/kairo.mp3'}
>>> f.__getitem__("plik") #(1)
'/music/_singles/kairo.mp3'
>>> f["plik"] #(2)
'/music/_singles/kairo.mp3'
- Metoda specjalna
__getitem__wygląda dość prosto. Ta metoda specjalna pozwala słownikowi zwrócić pewną wartość na podstawie podanego klucza. A jak tę 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. - Z takiej składni korzystamy, by dostać pewną wartość ze słownika. W rzeczywistości Python automatycznie przekształca taką składnię na wywołanie metody
f.__getitem__("plik"). Właśnie dlatego__getitem__nazywamy metodą specjalną: nie tylko możemy ją wywołać, ale Python wywołuje tę metodę także za nas, kiedy skorzystamy z odpowiedniej składni.
Istnieje także analogiczna do __getitem__ metoda __setitem__, która zamiast pobierać pewną wartość, zmienia daną wartość korzystając z pewnego klucza.
__setitem__
>>> f {'plik':'/music/_singles/kairo.mp3'} >>> f.__setitem__("gatunek", 31) #(1) >>> f {'plik':'/music/_singles/kairo.mp3', 'gatunek':31} >>> f["gatunek"] = 32 #(2) >>> f {'plik':'/music/_singles/kairo.mp3', 'gatunek':32}
- Analogicznie do
__getitem__, możemy za pomocą__setitem__zmienić wartość pewnego klucza znajdującego się w słowniku. Podobnie, jak w przypadku__getitem__nie musimy jej wywoływać w sposób bezpośredni. Python wywoła__setitem__, jeśli tylko uż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
f.__setitem__("gatunek", 32).
__setitem__ jest metodą specjalną, ponieważ Python wywołuje ją za nas, ale ciągle jest metodą klasy. Kiedy definiujemy klasy, możemy definiować pewne metody, nawet jeśli nadklasa ma już zdefiniowaną tę metodę. W ten sposób nadpisujemy (ang. override) metody nadklas. Tyczy się to także metod specjalnych.
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 metaatrybuty automatycznie. Możemy to zrobić poprzez nadpisanie metody __setitem__, w której sprawdzamy poszczególne klucze i jeśli dany klucz zostanie znaleziony, wykonujemy dodatkowe operacje.
Na przykład MP3FileInfo jest podklasą FileInfo. Kiedy w MP3FileInfo ustawiamy klucz "plik", nie tylko ustawiamy wartość samego klucza "plik" (jak to robi 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.
__setitem__ w klasie MP3FileInfo
def __setitem__(self, key, item): #(1)
if key == "plik" and item: #(2)
self.__parse(item) #(3)
FileInfo.__setitem__(self, key, item) #(4)
- Zwróćmy uwagę na kolejność i liczbę argumentów w
__setitem__. Pierwszym argumentem jest instancja danej klasy (argumentself), z której ta metoda została wywołana, następnym argumentem jest klucz (argumentkey), który chcemy ustawić, a trzecim jest wartość (argumentitem), którą chcemy skojarzyć z danym kluczem. Kolejność ta jest ważna, ponieważ Python będzie wywoływał tę metodą w takiej kolejności i z taką liczbą argumentów. (Nazwy argumentów nic nie znaczą, ważna jest ich ilość i kolejność.) - W tym miejscu zawarte jest sedno całej klasy MP3FileInfo: jeśli przypisujemy pewną wartość do klucza "plik", chcemy wykonać dodatkowo pewne operacje.
- Dodatkowe operacje dla klucza "plik" zawarte są w metodzie
__parse. Jest to inna metoda klasyMP3FileInfo. Kiedy wywołujemy metodę__parseużywamy zmiennejself. Gdybyśmy wywołali samo__parse, odnieślibyśmy się do normalnej funkcji, która jest zdefiniowana poza klasą, a tego nie chcemy wykonać. Kiedy natomiast wywołamyself.__parsebę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 obiektu. - Po wykonaniu tej dodatkowej operacji, chcemy wykonać metodę nadklasy. Pamiętajmy, że Python nigdy nie zrobi tego za nas; musimy zrobić to ręcznie. Zwróćmy uwagę na to, że odwołujemy się do bezpośredniej nadklasy, czyli do
FileInfo, chociaż on nie posiada żadnej metody o nazwie__setitem__. 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ę__setitem__, która jest zdefiniowana w samej wbudowanej klasie słownika, w klasiedict.
Kiedy odwołujemy się do danych zawartych w atrybucie instancji, musimy określić nazwę atrybutu np. self.attribute. Podczas wywoływania metody klasy, musimy określić nazwę metody np. self.method.
|
"plik" w MP3FileInfo
>>> import fileinfo >>> mp3file = fileinfo.MP3FileInfo() #(1) >>> mp3file {'plik':None} >>> mp3file["plik"] = "/music/_singles/kairo.mp3" #(2) >>> mp3file {'album': 'Rave Mix', 'rok': '2000', 'komentarz': 'http://mp3.com/DJMARYJANE', u'tytu\u0142': 'KAIRO****THE BEST GOA', 'artysta': '***DJ MARY-JANE***', 'gatunek': 31, 'plik': '/music/_singles/kairo.mp3'} >>> mp3file["plik"] = "/music/_singles/sidewinder.mp3" #(3) >>> mp3file {'album': '', 'rok': '2000', 'plik': '/music/_singles/sidewinder.mp3', 'komentarz': 'http://mp3.com/cynicproject', u'tytu\u0142': 'Sidewinder', 'artysta': 'The Cynic Project', 'gatunek': 18}
- Najpierw tworzymy instancję klasy
MP3FileInfobez podawania nazwy pliku. (Możemy tak zrobić, ponieważ argumentfilenamemetody__init__jest opcjonalny.) PonieważMP3FileInfonie posiada własnej metody__init__, Python idzie wyżej po drzewie nadklas i znajduje metodę__init__w klasieFileInfo. Z kolei__init__w tej klasie ręcznie wykonuje metodę__init__w klasiedict, a potem ustawia klucz"plik"na wartość w zmiennejfilename, który wynosiNone, ponieważ pominęliśmy nazwę pliku. Ostateczniemp3filepoczątkowo jest słownikiem (a właściwie instancją klasy potomnej słownika) z jednym kluczem"plik", którego wartość wynosiNone. - Teraz rozpoczyna się prawdziwa zabawa. Ustawiając klucz
"plik"wmp3filespowoduje wywołanie metody__setitem__klasyMP3FileInfo(a nie słownika, czyli klasydict). Z kolei metoda ta zauważa, że ustawiamy klucz"plik"z prawdziwą wartością (itemjest prawdą w kontekście logicznym) i wywołujeself.__parse. Chociaż jeszcze nie analizowaliśmy działania metody__parse, możemy na podstawie wyjścia zobaczyć, że ustawia ona kilka innych kluczy jak"album", "artysta","gatunek",u"tytuł"(w unikodzie, bo korzystamy z polskich znaków),"rok", czy też"komentarz". - Kiedy zmienimy klucz
"plik", proces ten zostanie wykonany ponownie. Python wywoła__setitem__, który następnie wywołaself.__parse, a ten ustawi wszystkie inne klucze.