Zanurkuj w Pythonie/Klasa opakowująca UserDict
Z Wikibooks, biblioteki wolnych podręczników.
[edytuj] Klasa opakowująca UserDict
Wrócimy na chwilę do przeszłości. Za czasów, kiedy nie można było dziedziczyć wbudowanych typów danych np. słownika, powstawały tzw. klasy opakowujące, które pełniły te same funkcję, co typy wbudowane, ale można je było dziedziczyć. Klasą opakowującą dla słownika była klasa UserDict, która nadal jest dostępna wraz z nowymi wersjami Pythona. Przyglądnięcie się implementacji tej klasy może być dla nas cenną lekcją. Zatem zajrzyjmy do kodu źródłowego klasy UserDict, który znajdują się w module UserDict. Moduł ten z kolei jest przechowywany w katalogu lib instalacji Pythona, a pełna nazwa pliku to UserDict.py (nazwa modułu z rozszerzeniem .py).
|
W IDE ActivePython na Windowsie możemy szybko otworzyć dowolny moduł, który znajduje się w ścieżce do bibliotek, gdy wybierzemy File->Locate... (Ctrl-L). |
UserDict
class UserDict: #(1)
def __init__(self, dict=None): #(2)
self.data = {} #(3)
if dict is not None: self.update(dict) #(4) (5)
- Klasa
UserDictnie dziedziczy nic z innych klas. Jednak nie patrzmy się na to, pamiętajmy, żeby zawsze dziedziczyć zobject(lub innego wbudowanego typu), bo wtedy mamy dostęp do dodatkowych możliwości, które dają nam klasy w nowym stylu. - Jak pamiętamy, metoda
__init__jest wywoływana bezpośrednio po utworzeniu instancji klasy. Przy tworzeniu instancji klasyUserDictmożemy zdefiniować początkowe wartości, poprzez przekazanie słownika w argumenciedict. - W Pythonie możemy tworzyć atrybuty danych (zwane polami w Javie i PowerBuilderze). Atrybuty to kawałki danych przechowywane w konkretnej instancji klasy (moglibyśmy je nazwać atrybutami instancji). W tym przypadku każda instancja klasy
UserDictbędzie posiadać atrybutdata. Aby odwołać się do tego pola z kodu spoza klasy, dodajemy z przodu nazwę instancji np.instancja.data; robimy to w identyczny sposób, jak odwołujemy się do funkcji poprzez nazwę modułu, w którym ta funkcja się znajduje. Aby odwołać się do atrybutu danych z wnętrza klasy, używamyself. Zazwyczaj wszystkie atrybuty są inicjalizowane sensownymi wartościami już w metodzie__init__. Jednak nie jest to wymagane, gdyż atrybuty, podobnie jak zmienne lokalne, są tworzone, gdy po raz pierwszy przypisze się do nich jakąś wartość. - Metoda
updatepowiela zachowanie metody słownika: kopiuje wszystkie klucze i wartości z jednego słownika do drugiego. Metoda ta nie czyści słownika docelowego (tego, z którego wywołaliśmy metodę), ale jeśli były tam już jakieś klucze, to zostaną one nadpisane tymi, które są w słowniku źródłowym; pozostałe klucze nie zmienią się. Myślmy oupdatejak o funkcji łączenia, nie kopiowania. - Z tej składni nie korzystaliśmy jeszcze w tej książce. Jest to instrukcja
if, ale zamiast wciętego bloku, który rozpoczynałby się w następnej linii, korzystamy tu z instrukcji, która znajduje się w jednej linii, zaraz za dwukropkiem. Jest to całkowicie poprawna, skrótowa składnia, której możemy używać, jeśli mamy tylko jedną instrukcję w bloku (tak jak pojedyncza instrukcja bez klamer w C++). Możemy albo skorzystać z tej skrótowej składni, albo tworzyć wcięte bloki, jednak nie możemy ich ze sobą łączyć w odniesieniu do tego samego bloku kodu.
UserDict
def clear(self): self.data.clear() #(1)
def copy(self): #(2)
if self.__class__ is UserDict: #(3)
return UserDict(self.data)
import copy #(4)
return copy.copy(self)
def keys(self): return self.data.keys() #(5)
def items(self): return self.data.items()
def values(self): return self.data.values()
clearjest normalną metodą klasy; jest dostępna publicznie i może być wołana przez kogokolwiek w dowolnej chwili. Zauważmy, że wclear, jak we wszystkich metodach klas, pierwszym argumentem jestself. (Pamiętajmy, że nie dodajemyself, gdy wywołujemy metodę; Python robi to za nas.) Zwróćmy uwagę na podstawową cechę tej klasy opakowującej: przechowuje ona prawdziwy słownik w atrybuciedatai definiuje wszystkie metody wbudowanego słownika, a w każdej z tych metod zwraca wynik identyczny do odpowiedniej metody słownika. (Gdybyśmy zapomnieli, metoda słownikaclearczyści cały słownik kasując jego wszystkie klucze i wartości.)- Metoda słownika o nazwie
copyzwraca nowy słownik, który jest dokładną kopią oryginału (mający takie same pary klucz-wartość). Natomiast klasaUserDictnie może po prostu wywołaćself.data.copy, ponieważ ta metoda zwraca wbudowany słownik, a my chcemy zwrócić nową instancję klasy tej samej klasy, jaką maself. - Używamy atrybutu
__class__, żeby sprawdzić, czyselfjest obiektem klasyUserDict; jeśli tak, to jesteśmy w domu, bo wiemy, jak zrobić kopięUserDict: tworzymy nowy obiektUserDicti przekazujemy mu słownik wyciągnięty zself.data, a wtedy możemy od razu zwrócić nowy obiektUserDictnie wykonując nawet instrukcjiimport copyz następnej linii. - Jeśli
self.__class__nie jestUserDict-em, toselfmusi być jakąś podklasąUserDict-a, a w takim przypadku życie wymaga użycia pewnych trików.UserDictnie 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 pętlę, podczas której kopiujemy każdy z tych atrybutów. Na szczęście istnieje moduł, który wykonuje dokładnie to samo, nazywa się oncopy. Nie będziemy się tutaj wdawać w szczegóły (choć jest to wypaśny moduł, jeśli się w niego trochę wgłębimy). Wystarczy wiedzieć, żecopypotrafi kopiować dowolne obiekty, a tu widzimy, jak możemy z niego skorzystać. - Pozostałe metody są bezpośrednimi przekierowaniami, które wywołują wbudowane metody z
self.data.
| Uwaga! Od Pythona 2.2 nie korzystamy z klasy |
[edytuj] Materiały dodatkowe
- Python Library Reference dokumentuje moduł
copy