Kurs Pythona - pypassion.com
Transkrypt
Kurs Pythona - pypassion.com
PROGRAMOWANIE PYTHON Kurs Pythona Odcinek 3: Model obiektowy Dwa pierwsze odcinki miały za zadanie zapoznać nas z podstawowymi cechami języka i środowiska Python. Ostatnią wielką niewiadomą pozostały dla nas klasy. Po ich poznaniu będziemy mogli uznać, że rozumiemy fundamenty Pythona. Do dzieła! Dowiesz się: Powinieneś wiedzieć: • Jak korzystać z wbudowanych oraz bibliotecznych klas i obiektów; • Jak zbudować własną klasę; • Czym są dekoratory. • Jak zainstalować i uruchomić interpreter Python 2.6 ; • Jak używać wbudowanych w Pythona list, słowników, krotek i zbiorów. M odel klasowy Pythona składa się z dość prostej składni i funkcjonalności, która jest mieszanką systemów obiektowych znanych w C++, Perlu i Objective-C. Od każdego z tych modeli różni się jednak na tyle, że warto przejść przez wszystkie cechy klas pythonowych dokładnie. Podstawowa klasa Zacznijmy od razu od przykładu. Na Listingu 1 znajduje się prosty model klasowy, który prezentuje wszystkie podstawowe cechy języka. Prezentowane klasy opisują odpowiednio "grzałkę", "dmuchawę" oraz ich połączenie, czyli "suszarkę do włosów". Zwróć uwagę, że mimo tego, że nie omówiłem jeszcze żadnej kwestii dotyczącej klas, już jesteś w stanie dużo zrozumieć. dami. O funkcjach zdążyliśmy sobie opowiedzieć sporo już ostatnim razem. Zdążyliśmy też wspomnieć o wbudowanych funkcjach i związanych z nimi funkcjach specjalnych mających w nazwie po dwa znaki "_" z przodu i z tyłu. W przypadku metod funkcje te nabierają szczególnego znaczenia, ponieważ pozwalają klasie implementować szereg opcjonalnych funkcjonalności takich jak ładna reprezentacja tekstowa w metodach __unicode__ i __str__ czy możliwość porównywania arytmetycznego ze sobą różnych obiektów danej klasy, w metodach __lt__ (mniejszy), __gt__ (większy), __eq__ (równy) itd. Jedną z takich metod specjalnych jest metoda __init__, nazywana w pewnym uproszczeniu konstruktorem [1]. To znaczy, że tworząc nowe obiekty, wywołujemy właśnie tę metodę: Róbta co chceta hairdryer = Hairdryer() Najbardziej fundamentalną cechą klas i obiektów w Pythonie jest to, że nie ma w nich podziału na składowe publiczne i prywatne. Oznacza to, że każdy użytkownik danego obiektu ma pełen dostęp do wszystkich metod i pól zadeklarowanych w danej klasie. Użytkownikom Javy, C++ i C# wyda się to nie do pomyślenia, jednak taka decyzja powoduje szereg bardzo pozytywnych skutków, o czym powiemy sobie później. Ustalanie, jakie składniki klasy należą do jej wewnętrznej implementacji, jest możliwe przez konwencję, zgodnie z którą nazwy takich składników poprzedza się pojedynczym znakiem "_". Metody Funkcje znajdujące się wewnątrz klasy nazywamy meto- 54 normal_heater = Heater() hot_heater = Heater(max_temp=80) Jak widać, pierwszym argumentem metody _ _ init _ _ i wszystkich innych metod w klasach pythonowych jest self, czyli zmienna wskazująca na aktualny obiekt. Zmienna ta jest wpisywana do argumentów automatycznie podczas wywołania metody, więc nie musimy jej dodatkowo podawać, np.: >>> hairdryer.dry(5) Heating to 40 degrees for 5 minutes. >>> hairdryer.max_temp 50 9/2010 Model obiektowy >>> hot_heater.max_temp 80 automatycznie dołączy do wykonania metody pierwszy argument self wskazujący na obiekt hairdryer. Argument 45 trafi więc do zmiennej how _ long. Niektóre języki umożliwiają przygotowywanie wielu wersji tej samej metody, różniących się typem lub liczbą argumentów. W świecie Pythona taka możliwość nie jest potrzebna ze względu na obecność argumentów opcjonalnych. Przykład takiego argumentu mamy w konstruktorze klasy Heater, który przez to możemy wołać zarówno nie podając żadnych dodatkowych argumentów, jak również specyfikując niestandardową temperaturę maksymalną. Pola W prezentowanym na Listingu 1 przykładzie widać dwa przykłady pól: klasa Heater prezentuje pola klasowe, czyli takie, których wartość jest wspólna dla wszystkich obiekListing 1. Podstawowa struktura klas class Heater(object): min_temp = 28 max_temp = 50 def __init__(self, max_temp=None): if max_temp: self.max_temp = max_temp def heat(self, temp, how_long): if self.min_temp <= temp <= self.max_temp: print "Heating to", temp, print "degrees for", how_long, "minutes." else: raise ValueError, "Temperature out or range." class Blower(object): def __init__(self): self.blowing = False def blow(self): self.blowing = True def stop(self): self.blowing = False class Hairdryer(Heater, Blower): def __init__(self): super(Hairdryer, self).__init__() def dry(self, how_long): self.blow() self.heat(40, how_long) www.sdjournal.org tów danej klasy. W klasie Blower w metodzie __init__() znajdziemy natomiast pola instancji, czyli takie, które są zawarte tylko w konkretnym obiekcie (czyli w pojedynczej instancji danej klasy). Inne języki posiadają podobne rozgraniczenie, nazywając często pola klasowe statycznymi. Ciekawą cechą, którą można zaobserwować w przykładzie, jest fakt, że w metodzie heat() klasy Heater odwołujemy się do pól klasowych tak, jak gdyby były one polami instancji. Jest to często używany skrót, który dodatkowo pozwala w razie potrzeby na przesłonienie danego pola klasowego polem instancji. Przykład takiej techniki znajduje się w konstruktorze klasy Heater, gdzie jeżeli podamy opcjonalny argument max_temp, konstruktor przesłoni nam domyślną wartość pola klasowego nowym polem instancji. Powoduje to, że klasa w ogólności zachowuje swoje domyślne ustawienie, ale obiekty mogą to ustawienie dostosowywać do własnych potrzeb. Dziedziczenie Jak widać w naszym przykładzie, Python obsługuje wielokrotne dziedziczenie. Jest to funkcjonalność stosunkowo rzadko wykorzystywana i głównie do "przyklejania" dodatkowej funkcjonalności do istniejących klas, tak też zrobiliśmy w naszym przykładzie, gdzie suszarka jest po prostu grzałką z dmuchawą. Klasy, po których dziedziczymy, wymieniamy po przecinku w nawiasach za nazwą klasy. Domyślnie, jeżeli nie chcemy dziedziczyć z jakiejś klasy, powinniśmy dziedziczyć z klasy object. Klasy, które nie dziedziczą po tym typie, są w świecie Pythona znane jako klasy starego stylu i posiadają szereg niewygodnych ograniczeń [2]. Stąd, jeżeli tylko możemy, powinniśmy dziedziczyć z object. Klasy, po których dziedziczymy w świecie Pythona, nazywamy klasami bazowymi. W każdym przypadku, kiedy Listing 2. Dziedziczenie z wbudowanego typu >>> class AttrDict(dict): ... ... def __getattr__(self, attr): ... ... ... try: return self[attr] except KeyError: return '' >>> a = AttrDict() >>> a['monty'] = 'python' >>> a['star'] = 'bucks' >>> a {'star': 'bucks', 'monty': 'python'} >>> a.star 'bucks' >>> a.monty 'python' >>> a.bill '' 55 PROGRAMOWANIE PYTHON chcemy odnieść się do jakiejś metody lub pola z klasy bazowej, możemy to zrobić bez żadnego kłopotu. Przykład widać w metodzie dry() klasy Hairdryer, która odwołuje się do metod blow() i heat() zdefiniowanych w klasach bazowych. Może się jednak zdarzyć sytuacja, że w aktualnej klasie definiujemy składnik o takiej samej nazwie jak występujący w klasie bazowej. Wówczas trzeba użyć konstrukcji super(), która umożliwia wykonanie metody lub uzyskanie dostępu do pola z klasy bazowej [3]. Po wbudowanych typach też można dziedziczyć, co prezentuje Listing 2. Mamy tam definicję magicznego typu, który jest jednocześnie słownikiem, ale także udostępnia elementy słownika jako swoje atrybuty. Jest to możliwe dzięki prostej implementacji specjalnej metody __getattr_ _. Dodatkowo w metodzie tej zapewniliśmy, że w przypadku odwołania się do nieistniejącego atrybutu otrzymamy pusty łańcuch znaków zamiast rzuconego wyjątku. Pola są lepsze niż metody W świecie Pythona bardzo często używaną cechą jest możliwość bezpośredniego odwołania się do pól danej klasy czy obiektu. Wywołania typu osoba.data_urodzenia lub samochod.silnik.pojemnoc są na porządku dziennym. Takie rozwiązanie ma szereg zalet, z których największą jest fakt, że tak pisany kod jest bardzo czytelny. Dodatkowo przez to, że odwołujemy się bezpośrednio do pola, wykonanie takiego żądania jest całkiem wydajne. Programiści Javy mogą w tym momencie zaalarmować, że takie zachowanie niesie za sobą zagrożenie! Mianowicie, możliwe, że później zmienimy architekturę naszej klasy i już nie będzie można bezpośrednio pobrać daty urodzenia danej osoby z pola, ale trzeba będzie wykonać metodę. W takim przypadku trzeba będzie edytować kod, który korzysta z danej klasy, a to jest czasochłonne i niebezpieczne. Na szczęście w Pythonie możemy się od tego odciąć poprzez właściwości, czyli specjalne pola, które tak naprawdę są metodkami wywołanymi za naszymi plecami. Przykład widzimy w Listingu 3. Za pomocą specjalnej konstrukcji nazywanej dekoratorem, którą poprzedzamy defi- Przypisy [1] Właściwie konstrukcją obiektu zajmuje się inna metoda _ _ new _ _ , a metoda _ _ init _ _ pozwala jedynie logicznie zainicjalizować dany obiekt. Jest to jednak zaawansowane zagadnienie i dla większości potrzeb można myśleć o _ _ init _ _ jako o konstruktorze obiektów danej klasy. [2] Jednym z takich ograniczeń jest brak dostępu do funkcji super(). [3] O tym, jaka to konkretnie klasa, definiuje zasada nazywająca się MRO (ang. Method Resolution Order). Jest to zaawansowany temat, dla ciekawskich: http:// www.enterthefoo.com/static/prez/mro/index.html lub http: //www.python.org/download/releases/2.3/mro/. 56 Listing 3. Właściwości >>> class Traditional(object): ... field = 'A' >>> t = Traditional() >>> t.field 'A' >>> t.field = 'B' >>> t.field 'B' >>> class Smart(object): ... ... ... ... ... ... ... _data = 'A' @property def field(self): return self._data @field.setter def field(self, value): self._data = value ... >>> s = Smart() >>> s.field 'A' >>> s.field = 'B' >>> s.field 'B' >>> class Smart(object): ... ... @property ... def field(self): return 'A' >>> s = Smart() >>> s.field 'A' nicję metody, oznaczamy ją jako właściwość, czyli metodkę przebraną za pole. Jak widać, można nawet definiować grupy metod, które umożliwiają nadawanie takim "polom" nowych wartości. Morał z tej historii jest taki, że należy korzystać, kiedy się tylko da, z tradycyjnych pól. W razie potrzeby można je zamienić właściwościami, co ma tę dodatkową zaletę, że sposób korzystania z takiej klasy pozostaje cały czas prosty. W następnym odcinku W następnym miesiącu wykorzystamy wiedzę, którą zgromadziliśmy do operacji na plikach i przetwarzania tekstu. Do zobaczenia za miesiąc! ŁUKASZ LANGA programuje w Pythonie od 8 lat, aktualnie współpracuje z STX Next, rozwijając oprogramowanie dla sektora bankowego. Prywatnie miłośnik muzyki fortepianowej, początkujący ojciec i mąż. Adres e-mail autora: [email protected]. 9/2010