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