Zanurkuj w Pythonie/Klasa opakowująca UserDict: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Nie podano opisu zmian
Robwolfe (dyskusja | edycje)
Nie podano opisu zmian
Linia 2:
== Klasa opakowująca <tt>UserDict</tt> ==
 
Klasa <tt>FileInfo</tt> działa podobnie do słownika, o czym mogliśmy się już wcześniej przekonać. Aby ją lepiej zgłębić przyjrzyjmy się klasie <tt>UserDict</tt> w module <tt>UserDict</tt>, która jest przodkiem klasy <tt>FileInfo</tt>. Nie ma w niej nic specjalnego; jest to klasa napisana w Pythonie i przechowywana w pliku o rozszerzeniu <tt class="lang-none">.py</tt>, przypominajak onakażdy inneinny kodykod Pythona. Moduł ten jest przechowywany w katalogu <tt>lib</tt>, którytwojej z kolei znajduje się w miejsce, gdzie masz zainstalowanegoinstalacji Pythona.
 
{{Infobox|
W <tt>ActivePython IDE onActivePython na WindowsWindowsie</tt> możesz szybko otwieraćotworzyć dowolny moduł znajdujący się w twojej ścieżce do bibliotek wybierając ''File->Locate... (Ctrl-L)''.}}
 
'''Przykład 5.9. Definicja klasy <tt>UserDict</tt>'''
Linia 16:
 
# Klasa <tt>UserDict</tt> jest klasą bazową, nie dziedziczy nic z innych klas.
# Metodę <tt>__init__</tt> nadpisaliśmy w klasie <tt>FileInfo</tt>. Zauważmy, że lista argumentów w klasie przodka jest różna niż w klasie potomka. JestTo jest ok. Każda podklasa może mieć własny zbiór argumentów, pod warunkiem, że metody przodka będą wywoływane z poprawnymi argumentami. Tutaj klasa przodka posiada możliwość zdefiniowania początkowych wartości (dzięki przekazaniu słownika w argumencie <tt>dict</tt>), jednak której klasa <tt>FileInfo</tt> nie wykorzystuje.
# Python wspiera właściwościatrybuty (zwane polami w Javie i PowerBuilderze). WłaściwościAtrybuty to kawałki danych przechowywane w konkretnej instancji klasy. W tym przypadku, każda instancja klasy <tt>UserDict</tt> będzie posiadać właściwośćatrybut <tt>data</tt>. By odwołać się do tego pola z kodu spoza klasy, dodajemy z przodu nazwę instancji - instancja.data, w ten sam sposób jak odwołujemy się do funkcji poprzez nazwę modułu w jakim się znajduje. By odwołać się do właściwościatrybutu z wnętrza klasy, używamy <tt>self</tt>. Zazwyczaj wszystkie właściwościatrybuty są inicjalizowane sensownymi wartościami w metodzie <tt>__init__</tt>. Jednak nie jest to wymagane, gdyż właściwościatrybuty tak jak zmienne lokalne są tworzone gdy pierwszy raz przypisuje się do nich wartość.
# metoda <tt>update</tt> jest duplikatorem słownika: kopiuje wszystkie klucze i wartości z jednego słownika do drugiego. Ta metoda nie czyści słownika docelowego; jeśli były tam już jakieś klucze, te, które są w słowniku źródłowym zostaną nadpisane ale pozostałe się nie zmienią. Myśl o <tt>update</tt> jak o funkcji łączenia, nie kopiowania.
# tej składni mogłeś wcześniej nie widzieć (nie używałem jej w przykładach z tej książki). Jest to instrukcja <tt>if</tt> ale zamiast wciętego bloku zaczynającego się w następnej linii jest tu pojedyncza instrukcja w tej samej linii, za dwukropkiem. Jest to całkowicie poprawna składnia, będąca tylko skrótem, którego możesz używać, jeśli masz tylko jedną instrukcję w bloku (tak jak pojedyncza instrukcja bez klamer w C++). Możesz użyć tej składni albo wciętego kodu w następnych liniach, ale nie możesz użyć obu składni w tym samym bloku kodu.
Linia 25:
 
{{Infobox|
Guido, pierwszy twórca Pythona, tak wyjaśnia zasłanianie funkcji: "Klasy pochodne mogą zasłonić metody klas bazowych. Ponieważ metody nie mają żadnych specjalnych przywilejów wołając inne metody tego samego obiektu, może okazać się, że metoda klasy bazowej wołająca inną metodę zdefiniowaną w tej samej klasie bazowej woła właściwie metodę klasy pochodnej która ją zasłania. (Dla programistów C++: wszystkie metody w Pythonie zachowują się jakby były wirtualne.)" Jeśli dla Ciebie nie ma to sensu (dla mnie osobiście jest to strasznie wkurzazagmatwane), możesz to zignorować. Uważałem, że należy o tym wspomnieć.}}
 
{{Uwaga|
Linia 44:
# <tt>clear</tt> jest normalną metodą klasy; jest dostępna publicznie i może być wołana przez kogokolwiek w dowolnej chwili. Zauważ, że <tt>clear</tt>, jak wszystkie metody klas, ma <tt>self</tt> jako pierwszy argument. (Pamiętaj, że nie dodajesz <tt>self</tt> gdy wywołujesz metodę; Python robi to za Ciebie.) Zauważ podstawową cechę tej opakowującej klasy: przechowuje prawdziwy słownik (<tt>data</tt>) jako atrybut danych i definiuje wszystkie metody, które ma prawdziwy słownik, a w każdej z tych metod zwraca wynik identyczny do odpowiedniej metody słownika. (Gdybyś zapomniał, metoda <tt>clear</tt> słownika kasuje jego wszystkie klucze i wartości.)
# metoda <tt>copy</tt> słownika zwraca nowy słownik, będący dokładną kopią oryginału (mający takie same pary klucz-wartość). Ale klasa <tt>UserDict</tt> nie może po prostu wywołać <tt>self.data.copy</tt>, ponieważ ta metoda zwraca słownik a to co chcemy tu zrobić, to zwrócenie nowej instancji klasy takiej, jak <tt>self</tt>.
# Używamy atrybutu <tt>class__class__</tt>, żeby sprawdzić, czy <tt>self</tt> jest obiektem klasy <tt>UserDict</tt>; jeśli tak, jesteś w domu, bo wiesz jak zrobić kopię <tt>UserDict</tt>: stwórz nowy obiekt <tt>UserDict</tt> i przekaż mu słownik wyciągnięty z <tt>self.data</tt>. Wtedy możesz od razu zwrócić nowy obiekt <tt>UserDict</tt> nie wykonując nawet instrukcji <tt>import copy</tt> z następnej linii.
# Jeśli <tt>self.__class__</tt> nie jest <tt>UserDict</tt>-em, to <tt>self</tt> musi być jakąś podklasą <tt>UserDict</tt>-a (jak być może <tt>FileInfo</tt>) <!-- in which case life gets trickier -->. <tt>UserDict</tt> nie wie, jak utworzyć dokładną kopię jednego ze swoich potomków. W tym celu możemy np. znając atrybuty zdefiniowane w podklasie, wykonać na nich iterację kopiując każdy z tych atrybutów. Na szczęście istnieje moduł, który wykonuje dokładnie to samo, nazywa się on <tt>copy</tt>. Nie będziemy się tutaj wdawać w szczegóły (choć jest to niezły (cool) moduł, jeśli wgłębisz się w niego samemu). Wystarczy wiedzieć, że <tt>copy</tt> potrafi kopiować dowolne obiekty, a tu widzimy, jak możemy z niego skorzystać.
# Pozostałe metody są bezpośrednimi przekierowaniami wywołującymi wbudowane metody na <tt>self.data</tt>.
 
{{Infobox|
1=W wersjach 2.2 i wcześniejszych nie można było bezpośrenio tworzyć podklas wbudowanych typów jak napisy, listy czy słowniki. Aby to zrekompensować, Python dostarcza klasy opakowujące, które naśladują zachowanie wbudowanych typów danych. Są to: <tt>UserString</tt>, <tt>UserList</tt>, andi <tt>UserDict</tt>. Wykorzystując kombinację normalnych i specjalnych metod, klasa <tt>UserDict</tt> doskonale imituje słownik. W Pythonie 2.2 i nowszym możemy dziedziczyć bezpośrednio z wbudowanych typów danych, jak np. <tt>dict</tt>, czyli słownik. Przykład tego możemy znaleśćznaleźć w przykładach dostarczonych wraz z tą książką, w pliku <tt class="lang-none">fileinfo_fromdict.py</tt>.
}}
 
W Pythonie możemy dziedziczyć bezpośrednio wbudowanez typywbudowanych typów danych, co pokazano w poniższym przykładzie. Tutaj mamy trzy różnice w porównaniu z wersją korzystającą z klasy <tt>UserDict</tt>.
 
'''Przykład 5.11. Dziedziczenie bezpośrednio z wbudowanej klasy <tt>dict</tt>'''
Linia 63:
 
# Pierwsza różnica polega na tym, że nie importujemy modułu <tt>UserDict</tt>, ponieważ <tt>dict</tt> jest wbudowanym typem danych i jest ciągle dostępny. Druga polega na tym, że dziedziczymy bezpośrednio z klasy <tt>dict</tt>, zamiast z klasy <tt>UserDict.UserDict</tt>.
# Trzecia różnica jest subtelna, lecz też bardzo ważna. PonieważZ powodu w jaki <tt>UserDict</tt> pracujejest wskonstruowany sposób wewnętrznywewnątrz, wymaga on, aby manualnieręcznie wywołać metodę <tt>__init__</tt>, która w odpowiedni sposób zainicjujezainicjalizuje swojąjego wewnętrznąwewnętrzne strukturęstruktury danych. Z kolei <tt>dict</tt> nie działa w ten sposób. Nie jest to klasa opakowująca i nie wymaga wyraźnejjawnej inicjalizacji.
 
<noinclude>