<!doctype linuxdoc system>

<article>
<!-- LyX 1.1 created this file. For more info see http://www.lyx.org/ -->
<title>
LWP: Lamerskie Wprowadzenie do Pythona
</title>
<author>
Artur Skura &lt;arturs@linuxpl.org&gt;
</author>
<author>
podziękowania dla Wojtka Warczakowskiego &lt;wowar@poczta.onet.pl&gt;
</author>
<author>
gdzieniegdzie tekst modyfikował Maciej "
MACiAS"
 Pilichowski &lt;macias@torun.pdi.net&gt;
</author>
<date>
07.07.2001
</date>
<p>
szablon DocBook; LyX 1.1.6fix2
</p>
<abstract>
Dokument ten przedstawia podstawowe właściwości potężnego języka,
 jakim jest Python. Przeznaczony jest dla nie-programistów i hobbystów.
 Proszę zgłaszać wszystkie błędy (zapewne jest ich mnóstwo) i nie
 irytować się łopatologią ;-)
</abstract>
<toc>
<sect>
Wstęp
<p>
Wiele osób pragnąc nauczyć się programować staje przed dylematem:
 "
Od jakiego języka programowania zacząć?"
. Pytanie jest dość istotne,
 ponieważ negatywne nawyki, którymi przesiąknie początkujący bardzo
 trudno potem wykorzenić.
</p>
<p>
Eric S. Raymond w swoim eseju "
Jak zostać hackerem?"
 proponuje
 Pythona. Istnieje wiele argumentów przemawiających za wykorzystaniem
 tego języka. Niektóre aspekty C (np. wskaźniki) mogą sprawić początkującym
 wiele trudności; utrzymanie porządku w programach napisanych w Perlu
 staje się po pewnym czasie bardzo trudne (podobnie jest z Tcl/Tk);
 Java wymaga zrozumienia pewnych podstaw, a narzędzia do niej -- wymagań
 sprzętowych wyższych niż przeciętne... Nauka Pythona jest rozsądnym
 kompromisem: z jednej strony otrzymujemy język, którego stosunkowo
 łatwo można się nauczyć, z drugiej zaś -- potężne narzędzie używane
 przez profesjonalistów, w swej podstawowej postaci bardzo użyteczne
 wszędzie tam, gdzie np. zachodzi konieczność wykonywania skomplikowanych
 operacji na łańcuchach znaków.
</p>
<p>
Twórca Pythona, Guido van Rossum, zaleca jego naukę na samym
 początku, przed wprowadzeniem do takich języków jak Java czy C++.
 Dzięki temu, po zakończeniu kursu uczeń będzie potrafił programować
 na przyzwoitym poziomie i rozumiał podstawy programowania obiektowego,
 co znacznie ułatwi mu naukę w/w języków.
</p>
<sect>
Zaczynamy
<p>
Aby zorientować się w możliwościach języka, uruchomimy interpreter
 Pythona:
</p>
<p>
<code>
&dollar; python
</code>
</p><p>
Pojawi się napis podobny do poniższego:
</p>
<p>
<code>
Python 1.5.2 (&num;1, Feb  1 2000, 16:32:16)  &lsqb;GCC egcs-2.91.66 19990314/Linux (egcs- on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
&gt;&gt;&gt; 
</code>
</p><p>
Możemy teraz wydawać polecenia, które zostaną wykonane od razu.
 Napiszmy:
</p>
<p>
<code>
&gt;&gt;&gt; print &quot;Mamusiu, jak tu pięknie!&quot;
</code>
</p><p>
W rezultacie powinniśmy otrzymać napis "
Mamusiu, jak tu pięknie!"
.
 Python zazwyczaj robi dokładnie to, co mu każemy. 
</p>
<sect1>
Wstępnych uwag parę
<sect2>
Zaczynamy gubić przykłady
<p>
Chociaż w dalszym tekście nadal jest zachowana konwencja wpisywania
 poleceń bezpośrednio interpreterowi Pythona, to jednak aby oszczędzić
 sobie czasu i nerwów możemy korzystając w edytorze tekstu (który
 pozwala na zapis plików ASCII) stworzyć plik o rozszerzeniu "
py"

 --- np. "
tescik.py"
. W nim możemy pisać program w Pythonie, a po
 zapisaniu wydać polecenie:
</p>
<p>
<code>
python tescik.py
</code>
</p><p>
Jeśli popełniliśmy tzw. głupi błąd --- literówkę --- to wracamy
 do edycji pliku i poprawiamy co trzeba.
</p>
<sect2>
Jak łatwo zauważyć...
<p>
<code>
print x&lsqb;zk&rsqb;*f/u&lsqb;ol&rsqb;,we&lsqb;d&lsqb;m&rsqb;&rsqb;*r
</code>
</p><p>
Ok, to i tak nie jest jeszcze największy galimatias jaki można
 uczynić z kodu. Python pozwala Ci opisać takie kawałki własnymi słowami
 --- aby odróżnić Twój opis od kodu programu użyj znaku "
&num;"
 i
 po nim wpisz komentarz. Np:
</p>
<p>
<code>
&num; o rrrany, w ogóle nie wiem co się tutaj dzieje
</code>
</p><p>
Ale tak naprawdę powinieneś pisać kod tak, aby był samodokumentujący
 się --- tj. aby faktycznie było "
łatwo zauważyć, że..."
 nawet bez
 dodatkowego komentarza z Twojej strony.
</p>
<sect2>
A bo mnie się shift nacisnął...
<p>
Jeszcze nie wiemy, jak Python działa, ale już na wstępie:
</p>
<p>
<code>
&gt;&gt;&gt; Q = 100
&gt;&gt;&gt; print q
</code>
</p><p>
dostajemy piękny błąd. Dla Pythona stanowi różnicę pisownia małymi
 i wielkimi literami!
</p>
<sect>
Wbudowane typy danych
<sect1>
Łańcuchy
<p>
Zaimplementowano w nim wiele cech innych języków eliminując związane
 z nimi niedogodności, dzięki czemu programista może skupić się na
 pracy a nie na semantyce.
</p>
<p>
Przypiszmy teraz zmiennej "
a"
 napis "
Dajcie mi kredki"
:
</p>
<p>
<code>
&gt;&gt;&gt; a = &quot;Dajcie mi kredki.&quot;
&gt;&gt;&gt; print a
Dajcie mi kredki.
</code>
</p><p>
Idźmy dalej: łączenie napisów:
</p>
<p>
<code>
&gt;&gt;&gt; a = &quot;Dajcie&quot;
&gt;&gt;&gt; b = &quot;mi w końcu te kredki!!!&quot;
&gt;&gt;&gt; print a, b
Dajcie mi w końcu te kredki!!!
</code>
</p><p>
Można też inaczej:
</p>
<p>
<code>
&gt;&gt;&gt; a = &quot;No, &quot;
&gt;&gt;&gt; b = &quot;wreszcie!&quot;
&gt;&gt;&gt; print a+b
No, wreszcie!
</code>
</p><p>
Można łączyć jeszcze bardziej:
</p>
<p>
<code>
&gt;&gt;&gt; print &quot;Jarek powiedział:&quot;, a + b
Jarek powiedział: No, Wreszcie!
</code>
</p><p>
Co ciekawe, łańcuchy można nie tylko dodawać -- można je również
 "
mnożyć"
:
</p>
<p>
<code>
&gt;&gt;&gt; s = &quot;Nie!&quot;
&gt;&gt;&gt; print 3*s
Nie!Nie!Nie!
</code>
</p><p>
Możemy także w prosty sposób zamienić tekst na liczbę (o ile
 ma to sens):
</p>
<p>
<code>
&gt;&gt;&gt; x = &quot;7&quot;
&gt;&gt;&gt; print x*3
777
&gt;&gt;&gt; x = eval(x)    &num; obliczenie wartości
&gt;&gt;&gt; print x*3
21
</code>
</p><p>
Gdybyśmy nie pracowali w trybie interaktywnym, a zapisywali kod
 do pliku, moglibyśmy przekształcenie zapisać prościej:
</p>
<p>
<code>
x = `x`    &num; odwrotne apostrofy, to samo co "
eval"

</code>
</p><p>
Ciekawym elementem Pythona są tzw. plasterki (ang. slices). Łańcuch
 znaków zostaje pocięty na "
plasterki"
, oznaczane nawiasami kwadratowymi.
 I tak łańcuch &lsqb;0&rsqb; oznacza pierwszy znak łańcucha, łańcuch
 &lsqb;1&rsqb; -- drugi, itd.:
</p>
<p>
<code>
&gt;&gt;&gt; s = &quot;lajkonik&quot;
&gt;&gt;&gt; print s&lsqb;0&rsqb;
l
&gt;&gt;&gt; print s&lsqb;1&rsqb;
a
</code>
</p><p>
Prawda, że proste? Python idzie nieco dalej -- ogólniejsze "
krojenie"

 polega na wskazaniu dolnego indeksu i górnego (w ten sposób dostaniemy
 się do fragmentu od wskazanego dolnego indeksu do górnego, <bf>ale bez
 niego</bf>). Jeśli opuścimy, któryś z indeksów, to Python przyjmie domyślnie
 skrajne wartości (odpowiednio -- 0 i długość łańcucha).
</p>
<p>
Łańcuch &lsqb;:2&rsqb; odnosi się do pierwszych dwóch znaków
 łańcucha (czyli znaków o indeksie 0 i 1), zaś łańcuch &lsqb;2:&rsqb;
 -- do wszystkiego oprócz pierwszych dwóch znaków:
</p>
<p>
<code>
&gt;&gt;&gt; print s&lsqb;2:&rsqb;
jkonik
&gt;&gt;&gt; print s&lsqb;:2&rsqb;
la
</code>
</p><p>
Jak można się domyślić, łańcuch &lsqb;1:5&rsqb; zwraca od drugiego
 do piątego (od znaku o indeksie 1 do znaku o indeksie 4) znaku łańcucha:
</p>
<p>
<code>
&gt;&gt;&gt; print s&lsqb;1:5&rsqb;
ajko
</code>
</p><p>
Nierzadko zdarza się, że końcowa pozycja interesuje nas w odniesieniu
 do długości podawanego łańcucha, np.:
</p>
<p>
<code>
&gt;&gt;&gt; print s&lsqb;:len(s)-1&rsqb;
lajkoni
</code>
</p><p>
Czyli dostaliśmy napis bez ostatniego znaku. Ale Python upraszcza
 nam życie jeszcze bardziej --- indeksować można z pominięciem obliczenia
 długości, podając samą (ujemną) wartość. Czyli:
</p>
<p>
<code>
&gt;&gt;&gt; print s&lsqb;:-1&rsqb;
lajkoni
</code>
</p><p>
Czy to jasne? Poprosiliśmy Pythona o podanie łańcucha począwszy
 od znaku o indeksie 0 (pierwszego) do przedostatniego.
</p>
<sect2>
Pisanie pod lupą
<p>
Powyżej używaliśmy polecenia print z rozmaitymi operatorami.
 Teraz opiszemy je nieco dokładniej.
</p>
<p>
<code>
print &quot;ala ma kota&quot;
</code>
</p><p>
wypisze podany tekst i wstawi znak &lsqb;enter&rsqb;. Jeżeli
 chcemy tego uniknąć musimy postawić na samym końcu podanego tekstu
 znak przecinka:
</p>
<p>
<code>
print &quot;ala ma kota&quot;,
</code>
</p><p>
A teraz spróbujmy w ten sposób:
</p>
<p>
<code>
print &quot;ala&quot;, &quot;ma&quot;,&quot;kota&quot;
</code>
</p><p>
Jak powinieneś zauważyć tekst zostanie wydrukowany ze spacjami
 pomiędzy wyrazami. Przecinek bowiem między podanymi napisami wstawia
 odstęp. A jeśli chcielibyśmy tego uniknąć? Voila:
</p>
<p>
<code>
print &quot;ala&quot;+&quot;ma&quot;+&quot;kota&quot;
</code>
</p><p>
dostaniemy zbitkę słów bez rozdzielającej spacji.
</p>
<sect1>
Liczby
<p>
Operacje na liczbach również odbywają się w sposób intuicyjny,
 Rozważmy prosty przykład:
</p>
<p>
<code>
&gt;&gt;&gt; a = 10
&gt;&gt;&gt; b = 10
&gt;&gt;&gt; print a, b
10 10
</code>
</p><p>
Dwom zmiennym, a i b, przypisaliśmy wartość dziesięć. Nie ma
 problemu ze standardowymi operacjami na liczbach:
</p>
<p>
<code>
&gt;&gt;&gt; print a+b, a-b, a*b, a/b
20 0 100 1
</code>
</p><p>
Zaokrąglanie:
</p>
<p>
<code>
&gt;&gt;&gt; round(10.5)
11.0
</code>
</p><p>
Potęgowanie:
</p>
<p>
<code>
&gt;&gt;&gt; pow(5,2)
25
</code>
</p><p>
Albo i tak:
</p>
<p>
<code>
&gt;&gt;&gt; 5 ** 2
25
</code>
</p><p>
Operacje bitowe (odpowiednio --- bitowe or, dopełnienie, przesunięcie
 w lewo):
</p>
<p>
<code>
&gt;&gt;&gt; 4|1, &tilde;4, 4&gt;&gt;1
(5, -5, 2)
</code>
</p><p>
Jeśli chcemy natomiast dokonać operacji logicznych, to musimy
 pisać pełnymi słowami (poniższy przykład nie ma znaczenia w sensie
 zdrowego rozsądku):
</p>
<p>
<code>
&gt;&gt;&gt; 4 or 2, 5 and 3
(4, 3)
</code>
</p><p>
Konwersja między różnymi systemami:
</p>
<p>
<code>
&gt;&gt;&gt; hex(10), oct(10)
('0xa', '012')
</code>
</p><p>
Od wersji Pythona w okolicach 2.0 można stosować nienadmiarowe
 dodawanie (znane dobrze z C):
</p>
<p>
<code>
&gt;&gt;&gt; x = 10
&gt;&gt;&gt; x += 5
&gt;&gt;&gt; print x
15
</code>
</p><p>
Zapis:
</p>
<p>
<code>
X operator= ...
</code>
</p><p>
jest skrótem od
</p>
<p>
<code>
X = X operator (...)
</code>
</p><p>
ale nie jest w 100&percnt; mu tożsamy (o tym później). W każdym
 razie mamy do dyspozycji wszystkie sensowne skrótowce istniejące
 w Pythonie także w wersji pełnej ("
+="
, "
-"
, "
*="
, etc. --- chyba
 nie ma potrzeby wymieniać tu całej menażerii?).
</p>
<sect1>
Listy
<p>
Listy przypominają nieco tablice w innych językach. Charakteryzują
 się umieszczeniem elementów w nawiasach kwadratowych. Rozważmy prosty
 przykład:
</p>
<p>
<code>
&gt;&gt;&gt; lista = &lsqb;&quot;Polak&quot;, &quot;Rosjanin&quot;, &quot;Niemiec&quot;&rsqb;
&gt;&gt;&gt; print lista
&lsqb;'Polak', 'Rosjanin', 'Niemiec'&rsqb;
</code>
</p><p>
Na listach możemy dokonywać standardowych operacji: dodawać nowe
 elementy, usuwać stare, sortować, wyłapywać fragmenty (tak jak dla
 napisów -- napis jest tak naprawdę listą znaków)... Jeśli wydamy
 polecenie dir(nazwa typu), otrzymamy listing nazw operacji, które
 można przeprowadzać na danym typie:
</p>
<p>
<code>
&gt;&gt;&gt; dir(lista)
&lsqb;'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'&rsqb;
&gt;&gt;&gt; lista.append(&quot;Maciej&quot;)
&gt;&gt;&gt; print lista
&lsqb;'Polak', 'Rosjanin', 'Niemiec', 'Maciej'&rsqb;
&gt;&gt;&gt; lista.sort() 
&gt;&gt;&gt; print lista
&lsqb;'Maciej', 'Niemiec', 'Polak', 'Rosjanin'&rsqb;
</code>
</p><p>
Co ciekawe, w Pythonie mamy dostęp do prostej "
dokumentacji"

 używanych funkcji przy pomocy atrybutu .__doc__. Np.:
</p>
<p>
<code>
&gt;&gt;&gt; print lista.append.__doc__
L.append(object) -- append object to end
</code>
</p><p>
...co się przydaje, jeśli nie jesteśmy pewni, jak użyć danej
 funkcji:
</p>
<p>
<code>
&gt;&gt;&gt; print lista
&lsqb;'Tytus', 'Romek', 'Atomek'&rsqb;
&gt;&gt;&gt; lista.insert.__doc__
'L.insert(index, object) -- insert object before index'
&gt;&gt;&gt; lista.insert(1, &quot;Szarik&quot;)
&gt;&gt;&gt; print lista    
&lsqb;'Tytus', 'Szarik', 'Romek', 'Atomek'&rsqb;
</code>
</p><p>
Jak widać używanie list jest o niebo prostsze niż w innych językach.
</p>
<sect1>
Zbiory (tuple) i słowniki
<p>
<em>Na marginesie: bardzo formalnie rzecz biorąc tłumaczenie słowa
 "
tuple"
 jako "
zbiór"
 nie jest poprawne; w polskojęzycznej literaturze
 stosuje się inne, bardziej poprawne merytorycznie zwroty, ale są
 one tak szkaradne, iż nie ośmielamy się ich przytoczyć nawet małą
 czcionką.</em>
</p>
<p>
Zbiór to typ danych bardzo przypominający listę, wizualnie różni
 się brakiem nawiasów.
</p>
<p>
<code>
&gt;&gt;&gt; kolejka = 'Jacek', 'Wacek', 'Pankracek'
&gt;&gt;&gt; print kolejka
('Jacek', 'Wacek', 'Pankracek')
&gt;&gt;&gt; print kolejka&lsqb;1&rsqb;
Wacek
</code>
</p><p>
W przeciwieństwie do list, wartości zbiorów są niezmienne:
</p>
<p>
<code>
&gt;&gt;&gt; kolejka&lsqb;1&rsqb; = 'Agatka'
Traceback (innermost last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in ?
TypeError: object doesn't support item assignment
</code>
</p><p>
Słowniki zaś przypominają tablice asocjacyjne: z każdą wartością
 jest skojarzony jakiś klucz. Kolejność w słowniku nie ma żadnego
 znaczenia, dlatego zwykłe indeksowanie nie zadziała. Najlepiej wyjaśnić
 to na przykładzie:
</p>
<p>
<code>
&gt;&gt;&gt; slownik = &lcub; 'Ania':96, 'Gosia':69&rcub;
&gt;&gt;&gt; slownik&lsqb;'Ania'&rsqb;                   
96
</code>
</p><p>
Zobaczmy, jakich funkcji możemy użyć, by manipulować słownikami:
</p>
<p>
<code>
&gt;&gt;&gt; dir(slownik)  
&lsqb;'clear', 'copy', 'get', 'has_key', 'items', 'keys', 'update', 'values'&rsqb;
</code>
</p><p>
Keys() służy do wyświetlania kluczy:
</p>
<p>
<code>
&gt;&gt;&gt; print slownik.keys()        
&lsqb;'Ania', 'Gosia'&rsqb;
</code>
</p><p>
Zaś values() do wyświetlania wartości:
</p>
<p>
<code>
&gt;&gt;&gt; print slownik.values()
&lsqb;96, 69&rsqb;
</code>
</p><p>
Do poszczególnych par możemy się dostać w ten sposób:
</p>
<p>
<code>
&gt;&gt;&gt; print slownik.items()&lsqb;1&rsqb;
('Gosia', 69)
</code>
</p><sect>
Podstawy sterowania programem
<p>
...odbywa się przy pomocy standardowych mechanizmów znanych z
 innych języków. Należy zauważyć, że w Pythonie indentacja ("
wcięcia"
)
 spełniają rolę ograniczników (&lcub;&rcub;, BEGIN..END) z innych
 języków.
</p>
<sect1>
Konstrukcje warunkowe
<sect2>
if (jeśli)
<p>
<code>
&gt;&gt;&gt; if 2+2==4:
...     print &quot;2+2=4!&quot;
... 
2+2=4!
</code>
</p><p>
Należy zwrócić uwagę na dwukropek kończący warunek oraz wcięcie
 poprzedzające instrukcję.
</p>
<sect2>
elif ("
jeśli natomiast..."
)
<p>
<code>
&gt;&gt;&gt; if 2+2==5:
...     print &quot;tego raczej nie wydrukujemy&quot;
... elif 2+2==4:
...     print &quot;a to właśnie powinniśmy zobaczyć&quot;
</code>
</p><p>
... 
</p>
<p>
Konstrukcje "
if-elif-elif-..."
 możemy ciągnąć dość długo -- w
 Pythonie zastępują one znane z innych języków "
case/switch"
.
</p>
<sect2>
else ("
a jeśli nie, to..."
)
<p>
<code>
&gt;&gt;&gt; if 2+2==4:
...     print &quot;Murphy powiedziałby...&quot;
... else:
...     print &quot;...że ten napis zostanie wyswietlony&quot;
</code>
</p><p>
... 
</p>
<p>
Murphy powiedziałby...
</p>
<p>
Ponownie warto zwrócić uwagę na wcięcia.
</p>
<sect1>
Konstrukcje iteracyjne
<sect2>
for ("
dla każdego x z przedziału..."
)
<p>
For jest zaimplementowana w Pythonie w nieco specyficzny sposób:
</p>
<p>
<code>
&gt;&gt;&gt; lista = &lsqb;'Tytus', 'Romek', 'Atomek'&rsqb; 
&gt;&gt;&gt; for i in lista:
...     print i
... 
Tytus
Romek
Atomek
</code>
</p><p>
Oznacza to ni mniej nie więcej: dla każdego i, przyjmującego
 kolejne wartości z listy lista, wykonaj instrukcję print. Natomiast
 standardowe przedziały tworzy się za pomocą funkcji range():
</p>
<p>
<code>
&gt;&gt;&gt; print range(10)
&lsqb;0, 1, 2, 3, 4, 5, 6, 7, 8, 9&rsqb;
&gt;&gt;&gt; print range(5,10)
&lsqb;5, 6, 7, 8, 9&rsqb;
</code>
</p><p>
A cała konstrukcja wygląda następująco:
</p>
<p>
<code>
&gt;&gt;&gt; for i in range (1,4):
...     print &quot;Jan Sobieski miał trzy pieski:&quot;, i
... 
Jan Sobieski miał trzy pieski: 1
Jan Sobieski miał trzy pieski: 2
Jan Sobieski miał trzy pieski: 3
</code>
</p><sect2>
while ("
dopóki"
)
<p>
<code>
&gt;&gt;&gt; while a == 1:
...     print &quot;OK&quot;
</code>
</p><p>
rezultat powyższego zależy od wartości a. Jeśli wcześniej ustawimy
 a=1, pętla będzie się ciągnąć w nieskończoność. Konstrukcja while
 może posiadać dość specyficznie określoną akcję kończącą:
</p>
<p>
<code>
a = 1
while a &lt; 10:
  a += 1 ; print a
else:
  print &quot;koniec&quot;
</code>
</p><p>
"
Else"
 wykona się niezależnie od tego, czy "
while"
 było wykonane
 choć jeden raz. Jaka jest w takim razie różnica dla poniższego przypadku?
</p>
<p>
<code>
a = 1
while a &lt; 10:
  a += 1 ; print a
print &quot;koniec&quot;
</code>
</p><p>
Niewielka w zasadzie --- inaczej jednak będzie wykonywane polecenie
 "
break"
, o którym za chwilę.
</p>
<p>
Konstrukcje for i while określa się żargonowo pętlami --- mamy
 pętlę for i pętlę while.
</p>
<sect2>
break
<p>
Polecenie to przydaje się, kiedy chcemy natychmiast przerwać
 wykonywanie pętli, np.:
</p>
<p>
<code>
for i in range(100):
  if i==50:
    break
  print i
</code>
</p><p>
Przykład kompletnie odrealniony --- niemniej jednak Python po
 dotarciu do i==50 zakończy wykonywanie pętli while. Polecenie "
break"

 dotyczy tej pętli, wewnątrz najbardziej której zostało umieszczone:
</p>
<p>
<code>
for i in range(100):
  while i&lt;k:
    wynik = wynik + (i*k)
    k = k - 1
  if i==50:
    break
  print wynik
</code>
</p><p>
W przykładzie powyżej "
break"
 odnosi się do pętli "
for"
, a nie
 "
while"
, ponieważ "
while"
 nie zawiera "
break"
, natomiast for już
 tak. I drugi:
</p>
<p>
<code>
for i in range(100):
  while i&lt;k:
    wynik = wynik + (i*k)
    if i==50:
      break
    k = k -1
  print wynik
</code>
</p><p>
Teraz z kolei "
while"
 jest najbliższą pętlą w stosunku do "
break"

 (w sensie obejmowania kontroli, a nie odległości w wierszach kodu)
 i to tę pętlę przerwie break.
</p>
<p>
Należy podkreślić, iż konstrukcja "
while-else"
 stanowi całość
 i polecenia "
break"
 opuszcza całą konstrukcję --- "
else"
 nie zostanie
 wykonane.
</p>
<sect2>
continue
<p>
Działanie analogiczne do break, tyle że pętla nie jest przerywana,
 a jedynie pomijany jest kod po "
continue"
 i pętla dalej kontynuuje
 działanie.
</p>
<sect2>
pass
<p>
Czyli "
nic nie rób"
. Nieraz przydatne.
</p>
<sect1>
Tajniki kontroli programu
<sect2>
Porównania
<p>
Pojedynczy znak równości "
="
 odpowiada za operację przypisania
 -- danej zmiennej przypisujemy jakąś wartość (niech X równa się Y).
 Natomiast dwa znaki równości "
=="
 to porównanie (czy X równa się
 Y?). Co innymi porównaniami -- co z nierównością? Oczywiście istnieje
 -- zapisujemy ją jako "
&lt;&gt;"
 (tak jak w Pascalu) bądź jako "
!="

 (jak w C). Mamy też do dyspozycji operatory nierówności: "
&lt;"
,
 "
&gt;"
, "
&lt;="
, "
&gt;="
, a co więcej -- możemy je łączyć, np.:
</p>
<p>
<code>
if 10&lt;=x&lt;=100:
    ...
</code>
</p><sect2>
Kiedy wiadomo, że to prawda?
<p>
Python dokonuje skróconego sprawdzania warunków --- jeśli w pewnym
 momencie okaże się, że wynik jest już ustalony, sprawdzanie jest
 przerywane. Np:
</p>
<p>
<code>
&gt;&gt;&gt; x = 4
&gt;&gt;&gt; y = 5
&gt;&gt;&gt; if (x == 4) or (y == 777):
...     print &quot;a jednak&quot;
</code>
</p><p>
Napis zostanie oczywiście wyświetlony, ale nie w tym rzecz. Zgodnie
 z sensem operatora "
or"
 tylko jedna z wartości musi być prawdziwa,
 aby wynik także był prawdziwy. W tym przypadku "
x"
 faktycznie jest
 równe 4, więc jest kompletnie nieważne co następuje po "
or"
. Python
 w tym miejscu przerywa badanie i wyświetla napis.
</p>
<sect1>
Ułożenie kodu programu
<sect2>
Wcięcia
<p>
Tak jak już było to powiedziane, wcięcia mówią o "
hierarchii"

 danego kodu --- wcięć możesz dokonywać przy pomocy spacji bądź tabulatora.
 Ilu spacji (tabulatorów)? Nieważne --- obyś tylko zachowywał konsekwentnie
 swoją własną jednostkę wcięć (piszący te słowa używa wielokrotności
 dwu spacji, bo uważa, iż tak ładniej, a poza tym przyzwyczaił się).
</p>
<p>
Czy tekst programu nie jest przez to bardziej "
goły"
? Być może
 --- ale realizuje w pełni zasadę WYSIWYG. I dużo trudniej programiście
 pomylić się w interpretacji zagnieżdżeń w porównaniu do innych języków.
</p>
<sect2>
Jednolinijkowce
<p>
Do tej pory widziałeś tak zapisany program:
</p>
<p>
<code>
x = 4
y = 10
z = 13
</code>
</p><p>
Można jednak zapisać je łącznie, przedzielając średnikiem:
</p>
<p>
<code>
x = 4 ; y = 10 ; z = 13
</code>
</p><p>
Swoją drogą, gdybyś miał zwinąć taki kod:
</p>
<p>
<code>
x = 20
y = 20
</code>
</p><p>
to możesz zapisać to w ten sposób:
</p>
<p>
<code>
x = y = 20
</code>
</p><p>
ponieważ operator przypisania jest przechodni.
</p>
<p>
Co więcej proste polecenia można także pisać po instrukcjach
 warunkowych i pętlach, np.:
</p>
<p>
<code>
for i in range(100): print i
</code>
</p><sect>
Funkcje
<sect1>
Podstawy
<p>
Tworzenie funkcji w Pythonie jest bardzo proste. Zaczynają się
 od słowa def, po którym następuje nazwa funkcji z nawiasami, w których
 opcjonalnie mogą się znajdować argumenty.
</p>
<p>
<code>
&gt;&gt;&gt; def dodaj(a,b):
...     return a+b
... 
&gt;&gt;&gt; dodaj(1,2)
3
</code>
</p><p>
Nie musimy martwić się o sposób przekazywania argumentów (?):
</p>
<p>
<code>
&gt;&gt;&gt; def zamien(a,b):
...     temp=a
...     a=b
...     b=temp
...     return a,b
... 
&gt;&gt;&gt; zamien(10,11)
(11, 10)
</code>
</p><sect1>
Argumenty
<sect2>
Argumenty domyślne
<p>
Funkcjom możemy przypisać domyślne argumenty:
</p>
<p>
<code>
&gt;&gt;&gt; def pytanie(odpowiedz=&quot;Tito&quot;):
...     return odpowiedz
... 
</code>
</p><p>
Jeśli teraz wywołamy funkcję z argumentem, zostanie on uwzględniony:
</p>
<p>
<code>
&gt;&gt;&gt; pytanie('Lenin')
'Lenin'
</code>
</p><p>
...a jeśli nie, przyjęta zostanie wartość domyślna:
</p>
<p>
<code>
&gt;&gt;&gt; pytanie()
'Tito'
</code>
</p><sect2>
Argumenty nazwane
<p>
Python pozwala na bardzo eleganckie przypisywanie wartości pewnym
 argumentom. Powiedzmy, że mamy taką oto funkcję:
</p>
<p>
<code>
&gt;&gt;&gt; def okno(x=0,y=0,szerokosc=400,wysokosc=200,ramka=1,kolor=kGranatowy,czcionka=Times)
</code>
</p><p>
I chcemy tę funkcję wywołać, ale w zasadzie wystarczają nam wartości
 domyślne za wyjątkiem czcionki. Oto jak możemy postąpić przy wywołaniu:
</p>
<p>
<code>
&gt;&gt;&gt; okno(czcionka=Arial)
</code>
</p><p>
Na marginesie --- jeśli programowaliście kiedyś na Amidze, może
 dostrzeżecie pewne podobieństwo do argumentów w postaci tag-list.
</p>
<sect1>
Dane proste i wskazywane
<p>
Popatrzmy na poniższy przykład:
</p>
<p>
<code>
&gt;&gt;&gt; def wypelnij_liste(lista):
...     lista = &lsqb;&quot;czyzby&quot;,&quot;kotek&quot;,&quot;zginal&quot;&rsqb;
...
&gt;&gt;&gt; lista = &lsqb;&quot;ala&quot;,&quot;ma&quot;,&quot;kotka&quot;&rsqb;
&gt;&gt;&gt; wypelnij_liste(lista)
&gt;&gt;&gt; print lista
</code>
</p><p>
Jak sądzisz, co zobaczysz na ekranie? To dziwne na pozór zachowanie
 łatwo zrozumieć, jeśli przyjmiesz, że listy, słowniki i zbiory nie
 zawierają danych --- one tylko na nie <bf>wskazują</bf>. Polecenie
</p>
<p>
<code>
lista = &lsqb;&quot;ala&quot;,&quot;ma&quot;,&quot;kotka&quot;&rsqb;
</code>
</p><p>
nie powoduje przechowania tych trzech napisów <bf>w</bf> zmiennej lista.
 Napisy są przechowane w pamięci komputera, a "
lista"
 na nie wskazuje
 (efekt użycia operatora przyrównania -- "
="
). Jeśli nie widzisz jeszcze
 różnicy, to pomyśl o tabliczce z napisem "
Toruń 100km"
 --- taka tabliczka
 nie zawiera w sobie całego miasta, ona tylko nań wskazuje!
</p>
<p>
Wywołanie funkcji nie zmienia tego obszaru pamięci (on nadal
 istnieje), tworzony jest jedynie nowy obszar pamięci z innymi danymi,
 zmienna "
lista"
 na chwilę wskazuje na nowy obszar, po czym wracając
 z funkcji przywraca oryginalne wskazanie.
</p>
<p>
Oczywiście z tego samego powodu w poniższym przykładzie zmieni
 się nam właściciel futrzaka:
</p>
<p>
<code>
&gt;&gt;&gt; def wypelnij_liste(lista):
...     lista&lsqb;0&rsqb; = &quot;tomek&quot;
...
&gt;&gt;&gt; lista = &lsqb;&quot;ala&quot;,&quot;ma&quot;,&quot;kotka&quot;&rsqb;
&gt;&gt;&gt; wypelnij_liste(lista)
&gt;&gt;&gt; print lista
</code>
</p><p>
Tym razem nie ingerowaliśmy we wskazanie do danych (które miałoby
 i tak efekt lokalny), ale dokładnie w obszar pamięci. No dobrze,
 ale jak zabezpieczyć się przed zmianami na liście? To proste -- skopiować
 obszar danych, np. tak jak poniżej:
</p>
<p>
<code>
&gt;&gt;&gt; def wypelnij_liste(lista_arg):
...     lista = lista_arg&lsqb;:&rsqb;    &num; tutaj tworzymy <bf>kopię</bf> argumentu
...     lista&lsqb;0&rsqb; = &quot;tomek&quot;
...
&gt;&gt;&gt; lista = &lsqb;&quot;ala&quot;,&quot;ma&quot;,&quot;kotka&quot;&rsqb;
&gt;&gt;&gt; wypelnij_liste(lista)
&gt;&gt;&gt; print lista
</code>
</p><p>
Tym razem kotek nie zmienił właściciela.
</p>
<p>
Jeśli programujesz np. w C, to może już widzisz pełną analogię
 między traktowaniem argumentów struktur w Pythonie, a przekazywaniem
 w C argumentu jako wskaźnika do danych (np. klasyczne równouprawnienie
 tablicy i wskaźnika do niej; w pewnym sensie rzecz jasna). W ogólności
 w Pythonie istnieją argumenty niemodyfikowalne (czyli odpowiadające
 w C przekazywaniu przez wartość) oraz modyfikowalne, zmienne (odpowiadające
 w C przekazywaniu przez wskaźnik).
</p>
<sect1>
Zasięg nazw
<p>
Python w doborze nazw zmiennych kieruje się zasadą --- "
bliższa
 koszula ciału"
. Oznacza, to że jeśli jest to możliwe, Python będzie
 interpretował zmienną lokalnie, jeśli nie --- globalnie, a i jeśli
 tu nie --- jak wbudowaną. Pamiętając o tym, iż Python dynamicznie
 tworzy zmienne, przyjrzyjmy się następującym wariantom kodu:
</p>
<p>
<code>
tytul = &quot;ala&quot;

def zmien():
  tytul = &quot;zuzia&quot;    &num; ten wiersz będziemy omawiać
  print &quot;funkcja&quot;, tytul

zmien()
print &quot;program&quot;, tytul
</code>
</p><p>
W oznaczonym przypisaniu będzie użyta zmienna lokalna. Polecenie
 to może wykreować zmienną lokalną, więc w zgodzie z zasadą wyboru
 zasięgu, Python ją wykreuje. Jeśli chcielibyśmy odwołać się tu do
 zmiennej globalnej powinniśmy zacząć funkcję tak:
</p>
<p>
<code>
def zmien():
  global tytul
</code>
</p><p>
Polecenie "
global"
 zmienia interpretację dla wskazanej zmiennej.
 Nie musimy stosować polecenia "
global"
, jeśli użycie zmiennej wymusza
 taką właśnie interpretację:
</p>
<p>
<code>
def zmien():
  podtytul = tytul + &quot; ma kota&quot;
  print &quot;funkcja&quot;, podtytul
</code>
</p><p>
W tym przypadku nie ma cienia wątpliwości, iż chodzi o zmienną
 globalną "
tytul"
 --- lokalna interpretacja jest niepoprawna, gdyż
 brak jest przypisania (do zmiennej "
tytul"
). A co w takim przypadku:
</p>
<p>
<code>
def zmien():
  tytul = tytul + &quot; ma kota&quot;
  print &quot;funkcja&quot;, tytul
</code>
</p><p>
Dostaniemy błąd --- lewa część przypisania wskazuje na użycie
 lokalne, ale prawa na globalne. Przeważy interpretacja lokalna (dla
 całego wiersza) i przy próbie wykonania program oburzy się, iż próbowaliśmy
 użyć nieokreślonej zmiennej (rzecz dotyczy prawej części przypisania).
</p>
<sect>
Moduły
<p>
Moduły zawierają wiele dodatkowych funkcji. Wraz z Pythonem dostarczanych
 jest wiele modułów, oferujących ogromne choć rzadko doceniane możliwości.
 Ładuje się je przy pomocy polecenia import.
</p>
<sect1>
string -- łańcuchy znaków
<p>
Moduł string dostarcza wielu funkcji do manipulacji łańcuchami
 znaków. Zaimportujmy go:
</p>
<p>
<code>
&gt;&gt;&gt; import string
</code>
</p><p>
i zobaczmy, co w sobie kryje:
</p>
<p>
<code>
&gt;&gt;&gt; dir(string)
&lsqb;'__builtins__', '__doc__', '__file__', '__name__', '_idmap', '_idmapL', '_lower', '_re', '_safe_env', '_swapcase', '_upper', 'atof', 'atof_error', 'atoi', 'atoi_error', 'atol', 'atol_error', 'capitalize', 'capwords', 'center', 'count', 'digits', 'expandtabs', 'find', 'hexdigits', 'index', 'index_error', 'join', 'joinfields', 'letters', 'ljust', 'lower', 'lowercase', 'lstrip', 'maketrans', 'octdigits', 'replace', 'rfind', 'rindex', 'rjust', 'rstrip', 'split', 'splitfields', 'strip', 'swapcase', 'translate', 'upper', 'uppercase', 'whitespace', 'zfill'&rsqb;
</code>
</p><p>
Istnieją dwie możliwości: albo zaimportujemy moduł poleceniem
 import nazwa_modułu, i wtedy musimy wywoływać funkcję w postacinazwa_modułu.nazwa_funkcji,
 albo zaimportujemy je do użycia w sposób przezroczysty, za pomocą
 polecenia from nazwa_modułu import nazwa_funkcji, np.:
</p>
<p>
<code>
&gt;&gt;&gt; from string import upper
</code>
</p><p>
Możemy też od razu zaimportować wszystkie: 
</p>
<p>
<code>
&gt;&gt;&gt; from string import *
</code>
</p><p>
Wypróbujmy kilku funkcji:
</p>
<p>
<code>
&gt;&gt;&gt; a = &quot;Kiedy byłem mały&quot;
&gt;&gt;&gt; print upper(a)
KIEDY BYŁEM MAŁY
</code>
</p><p>
Możemy uczynić wielką pierwszą literę ciągu:
</p>
<p>
<code>
&gt;&gt;&gt; print capitalize(a)
</code>
</p><p>
Kiedy byłem mały
</p>
<p>
Albo każdego słowa:
</p>
<p>
<code>
&gt;&gt;&gt; print capwords(a)
Kiedy Byłem Mały
</code>
</p><p>
Uwaga: wszystkie te operacje są wykonywane na kopii łańcucha,
 nie na nim samym. Jeśli chcemy poddawać łańcuch dalszym modyfikacjom,
 najlepiej posłużyć się pomocniczymi zmiennymi. bardzo często używaną
 funkcją jest split(), która "
rozszczepia"
 łańcuch znaków na wyrazy:
</p>
<p>
<code>
&gt;&gt;&gt; b=split(a)    
&gt;&gt;&gt; print b&lsqb;0&rsqb;, b&lsqb;2&rsqb;
Kiedy mały
</code>
</p><p>
Na której pozycji w łańcuchu znajduje się pierwszy znak 'y'?
</p>
<p>
<code>
&gt;&gt;&gt; find(a,'y')
4
</code>
</p><p>
Ile razy występuje w ciągu?
</p>
<p>
<code>
&gt;&gt;&gt; count(a,'y')
3
</code>
</p><sect1>
re i regex --- wyrażenia regularne
<p>
Wyrażenia regularne pozwalają na odnalezienie znaków pasujących
 do podanego wzorca.
</p>
<p>
<code>
&gt;&gt;&gt; import re
</code>
</p><p>
Znajdźmy wszystkie przypadki wystąpienia znaku 'a':
</p>
<p>
<code>
&gt;&gt;&gt; re.findall(&quot;a&quot;, 'Jacek Klucha')
&lsqb;'a', 'a'&rsqb;
</code>
</p><p>
...znaku 'a' z następującym bezpośrednio po nim innym znaku:
</p>
<p>
<code>
&gt;&gt;&gt; re.findall(&quot;a.&quot;, 'Jacek Klucha')
&lsqb;'ac'&rsqb;
</code>
</p><p>
...znak 'J' na początku linii:
</p>
<p>
<code>
&gt;&gt;&gt; re.findall(&quot;^J&quot;, 'Jacek Klucha')
&lsqb;'J'&rsqb;
</code>
</p><p>
...ciąg 'cha' na końcu linii:
</p>
<p>
<code>
&gt;&gt;&gt; re.findall(&quot;cha&dollar;&quot;, 'Jacek Klucha')
&lsqb;'cha'&rsqb;
</code>
</p><p>
...znak 'a', po nim znak z zakresu a-d, następnie 'e':
</p>
<p>
<code>
&gt;&gt;&gt; re.findall(&quot;a&lsqb;a-d&rsqb;e&quot;, 'Jacek Klucha')
&lsqb;'ace'&rsqb;
</code>
</p><p>
Czasem użyteczne jest rozbicie ciągu na elementy przy użyciu
 arbitralnego ogranicznika (np. przy imporcie plików CSV):
</p>
<p>
<code>
&gt;&gt;&gt; re.split(&quot;m&quot;, &quot;Mamma mia&quot;)
&lsqb;'Ma', '', 'a ', 'ia'&rsqb;
</code>
</p><p>
Czasem można napotkać wykorzystanie przestarzałego modułu regex:
</p>
<p>
<code>
&gt;&gt;&gt; import regex
&gt;&gt;&gt; regex.match('a', 'ala')
1
&gt;&gt;&gt; regex.match('b', 'ala')
-1
</code>
</p><sect1>
urllib
<p>
urllib to bardzo przydatna biblioteka do ściągania stron WWW.
 Ale nie tylko. Rozpatrzmy prosty przykład: ściągnięcie strony www.tpnet.pl.
</p>
<p>
<code>
&gt;&gt;&gt; from urllib import *
</code>
</p><p>
Obiekt 'link' wskazuje na metodę open() klasy URLopener, głównej
 klasy zdefiniowanej w urllib.py:
</p>
<p>
<code>
&gt;&gt;&gt; link = URLopener().open('http://www.tpnet.pl')
</code>
</p><p>
...czekamy chwilę. Jeśli wszystko poszło OK, po napisaniu
</p>
<p>
<code>
&gt;&gt;&gt; print link
</code>
</p><p>
powinnismy otrzymać:
</p>
<p>
<code>
&lt;addinfourl at 135299432 whose fp = &lt;open file '&lt;socket&gt;', mode 'rb' at 80b3b20&gt;&gt;
</code>
</p><p>
Strona została ściągnięta, możemy się do niej dostać przy użyciustandardowej
 funkcji readlines:
</p>
<p>
<code>
&gt;&gt;&gt; print link.readlines()
</code>
</p><p>
(oszczędźmy sobie wypisywania kodu HTML, który i tak będziemy
 zapewne chcieli przerobić).
</p>
<p>
Ale to nie wszystko. Obiekt link posiada bowiem również metodę
 info(), pozwalającą uzyskać informacje o połączeniu:
</p>
<p>
<code>
&gt;&gt;&gt; print link.info()
Date: Mon, 17 Jul 2000 15:09:52 GMT
Server: Apache/1.3.9 (Unix)
Last-Modified: Thu, 20 Apr 2000 09:30:14 GMT
ETag: &quot;2983e-1a16-38fece26&quot;
Accept-Ranges: bytes
Content-Length: 6678
Connection: close
Content-Type: text/html
</code>
</p><p>
Możemy więc napisać proste narzędzie do badania rodzaju serwera
 WWW ;-). Ponieważ pole info nie jest napisem, tylko obiektem klasy
 Mimetools, dostajemy się do niego przy pomocy metody getheader():
</p>
<p>
<code>
&gt;&gt;&gt; print link.info().getheader('Server')
Apache/1.3.9 (Unix)
</code>
</p><p>
A oto przykład skryptu, który wyświetli nam wszystkie tytuły
 wiadomości z listy dyskusyjnej LinuxPL:
</p>
<p>
<code>
&num;!/usr/bin/env python
from urllib import *
import re
 
a = URLopener().open(&quot;http://linux.cgs.pl/linuxpl/2000-07/index.html&quot;)
&num;można też urllib.urlopen()
b = a.read()
c = re.findall(r'&bsol;&lsqb;linuxpl&bsol;&rsqb;.*.&lt;/STRONG',b)
for i in range(len(c)):
    d = c&lsqb;i&rsqb;
    print d&lsqb;9:(len(d)-8)&rsqb;
</code>
</p><sect>
Programowanie obiektowe
<sect1>
Klasy i obiekty
<p>
Pojęcie klasy jest pojęciem abstrakcyjnym, możemy go przyrównać
 do przepisu na pewien produkt --- np. opis windy. Opis windy nie
 jest tym samym co faktycznie realna i namacalna winda, ale czytając
 opis dowiemy się o przyciskach, o tym, że winda ma drzwi, o sytuacji
 awaryjnej. Taki opis to klasa.
</p>
<p>
Trzymając się naszej przenośni --- obiekt to konkretnie zbudowana
 winda. Już nie opis, ale faktyczna stalowa klatka zawieszona na linach.
 W klasie mówimy Pythonowi <bf>jak</bf> zbudować obiekt, ale to nie klasa "
działa"

 a obiekt.
</p>
<p>
Mówiąc krótko --- klasa jest abstraktem, a obiekt jej (klasy)
 instancją.
</p>
<sect2>
Metody i pola
<p>
Możemy zdefiniować (opisać) np. klasę "
Kwadrat"
 nie odnoszącą
 się do żadnego konkretnego kwadratu, tylko do kwadratów w ogólności.
 Rozważmy poniższy skrypt:
</p>
<p>
<code>
&num;!/usr/bin/env python
&num; Powyżej standardowy początek skryptów w Pythonie w systemach typu Unix
 
&num; definiujemy klasę Kwadrat:
class Kwadrat:
  bok = 0  

&num; do tego metodę podającą wartość boku
  def podaj_bok(self):
    return self.bok
</code>
</p><p>
Po kolei --- jak widać nasza klasa zawiera jedno pole o nazwie
 "
bok"
 (to tak jakby nasza winda posiadała tylko jedną cechę --- np.
 drzwi). Pod tym zapisaliśmy funkcję o nazwie "
podaj_bok"
, która jest
 częścią klasy "
Kwadrat"
 --- funkcje składowe klasy nazywamy metodami.
</p>
<p>
W definicji metody pierwszym argumentem musi być słowo kluczowe
 "
self"
 --- powie ono nam w odniesieniu do jakiego obiektu danej klasy
 została wywołana metoda. Jest to także potrzebne, aby prawidłowo
 odnieść się do własnych pól, co możemy zobaczyć w ostatnim wierszu.
</p>
<p>
A jak to działa?
</p>
<p>
<code>
kw = Kwadrat()
kw.bok = 14
print kw.podaj_bok() 
</code>
</p><p>
I już! Ważna uwaga --- Python sam wstawia nasz obiekt jak argument
 wywoływanej metody, sami w wywołaniu już go pomijamy. Technicznie
 rzecz biorąc moglibyśmy się odnieść do klasy i wywołać z tego poziomu
 metodę explicite podając obiekt:
</p>
<p>
<code>
print Kwadrat.podaj_bok(kw)
</code>
</p><p>
co jest tożsame z powyższym wywołaniem, ale jest dużo mniej eleganckie
 i dlatego nie będziemy takiego wywołania stosować (do czasu).

</p>
<p>
Obiekty są od siebie niezależne, to tylko różne instancje tej
 samej klasy. Popatrzmy na poniższy kod:
</p>
<p>
<code>
a = Kwadrat()
b = Kwadrat()
a.bok = 14
b.bok = 300
print a.podaj_bok(), b.podaj_bok()
</code>
</p><p>
Otrzymamy dwie różne wartości --- czego należało się oczywiście
 spodziewać.
</p>
<sect2>
Pułapki klas --- inicjowanie pól klasy
<p>
Pułapki --- bo przecież diabeł tkwi w szczegółach. Napisaliśmy,
 że klasa jest abstraktem, ale nie oznacza to, że klasa całkowicie
 nie może działać sama z siebie. Otóż --- może! I ma to całkiem znaczące
 konsekwencje:
</p>
<p>
<code>
class Kwadrat:
  bok = 0
</code>
</p><p>
Czy już może domyślasz się w czym rzecz? I owszem --- Python
 przemiatając kod programu uzna, iż klasa "
Kwadrat"
 na dzień dobry
 przypisuje polu "
bok"
 wartość zero. Oczywiście odbije się to na wszystkich
 obiektach tej klasy, ale ważne jest, że wartość pola nie zostanie
 ustalona w momencie powstania obiektu:
</p>
<p>
<code>
kw = Kwadrat()
</code>
</p><p>
To dzieje się wcześniej --- w momencie kiedy opisaliśmy klasę!
 Mimo, iż nie ma jeszcze żadnych obiektów. Czy to coś strasznego?
 Absolutnie nie, to wiadomość jak każda inna, należy tylko wiedzieć
 co się pisze --- w przypadku pól prostych nie trzeba sobie nawet
 zaprzątać głowy, ale w przypadku np. list:
</p>
<p>
<code>
class KlasaListy:
  lista = &lsqb;&rsqb;
</code>
</p><p>
I tutaj dokładnie wkraczamy na grząski grunt --- ponieważ zmienna
 listowa jest <bf>wskazaniem</bf> na właściwą listę, to wszystkie obiekty tej
 klasy będą współużytkowały tę samą listę!!! O ile nie wykonamy dodatkowego
 przypisania. Oto ilustracja:
</p>
<p>
<code>
a = KlasaListy()
b = KlasaListy()
&num; dołączenie, wskazanie pozostaje niezmienione
a.lista.append(&quot;zuzia&quot;)
print a.lista, b.lista
</code>
</p><p>
Uzyskamy dwie identyczne listy. To dlatego, iż żaden z obiektów
 nie zmienił tego nieszczęsnego przypisania w definicji klasy. Ale
 tutaj:
</p>
<p>
<code>
a = KlasaListy()
b = KlasaListy()
&num; określenie nowych wskazań
a.lista = &lsqb;&quot;zuzia&quot;&rsqb;
b.lista = &lsqb;&quot;kotek&quot;&rsqb;
print a.lista, b.lista
</code>
</p><p>
otrzymamy dwie różne listy, dlatego, iż każdy z obiektów wykorzystuje
 własne przypisanie (wskazanie). Tu uwaga na marginesie --- używanie
 obiektów bez zmiany wskazania jest analogiczne do pól statycznych
 klas w C++.
</p>
<p>
No dobrze, ale czy programista jest zmuszony do sztucznego przypisywania
 dla każdego z obiektów tylko po to, aby uniknąć konfliktów z innymi
 obiektami tej samej klasy? Nie, w takich przypadkach (tj. kiedy taki
 efekt jest niepożądany) należy w definicji klasy nie umieszczać przypisań.
</p>
<p>
<code>
class KlasaListy:
  &num; zupełne pominięcie <bf>inicjowania pól klasy</bf>
  def kopiuj(self,l):
    self.lista = l&lsqb;:&rsqb;

a = KlasaListy()
a.kopiuj(&lsqb;&quot;ala&quot;&rsqb;)
print a.lista
</code>
</p><sect2>
Inicjowanie pól obiektu
<p>
O inicjowaniu pól klasy wiemy już sporo, ale jeszcze nie wspomnieliśmy
 nic o inicjowaliśmy pól na poziomie obiektu, czyli o chwili, kiedy
 wykonywane jest np. poniższe polecenie:
</p>
<p>
<code>
a = KlasaListy()   &num; obiekt o nazwie "
a"
 rozpoczyna życie
</code>
</p><p>
Python zapewnia metodę o specjalnej nazwie "
__init__"
 (2 podkreślenia,
 słowo "
init"
 i znowu 2 podkreślenia) --- jest ona wykonywana w momencie,
 kiedy przywołujemy obiekt do życia.
</p>
<p>
<code>
class KlasaListy:
  def __init__(self):
    print &quot;hello, właśnie się narodziłem&quot;

a = KlasaListy()    &num; niejawne wywołanie metody "
__init__"

</code>
</p><sect2>
"
Self"
, czyli ten właśnie obiekt
<p>
Argument "
self"
 wpisywany do wszystkich metodach klasy to nazwa
 jak każda inna --- nie jest to słowo zastrzeżone, a nazwa. Jeśli
 odpowiada Wam bardziej "
this"
 nie ma żadnych przeszkód, aby tak właśnie
 nie pisać.
</p>
<p>
<code>
class KlasaListy:
  def pokaz_liste(ten_obiekt):
    print ten_obiekt.lista
</code>
</p><sect1>
Dziedziczenie
<p>
Klasy jako takie są już silnym narzędziem --- pozwalają bardzo
 ładnie organizować kod, wiązać pewne zmienne z funkcjami, które na
 nich operują. Służy to dobrej organizacji i wydajniejszej konserwacji
 oprogramowania. Ale klasy to nie tylko pojedyncze, niezależne definicje
 --- klasy można opisywać na bazie już istniejących klas.
</p>
<p>
Dodajmy do naszej klasy Kwadrat metodę obliczającą pole:
</p>
<p>
<code>
  def podaj_pole(self):
    return self.bok ** 2
</code>
</p><p>
Nic nadzwyczajnego prawda? Ale przecież na kwadratach świat się
 nie kończy --- co jeśli zechcielibyśmy napisać klasę sześcianu? Pisać
 wszystko od nowa? Oczywiście, można użyć metody copy&amp;paste w
 edytorze, ale Python oferuje coś więcej --- dziedziczenie! Dziedziczenie
 czyli bazowanie na wskazanej klasie i rozszerzanie jej funkcjonalności
 w klasie pochodnej. Popatrzmy:
</p>
<p>
<code>
class Szescian(Kwadrat):
  ....
</code>
</p><p>
Taki zapis mówi nam, iż klasa Szescian przyjmuje cały dobytek
 --- metody i pola --- klasy Kwadrat. Wystarczyłoby dopisać:
</p>
<p>
<code>
  pass
</code>
</p><p>
i już możemy używać klasy Szescian:
</p>
<p>
<code>
a = Szescian()
a.bok = 10
print a.podaj_pole()
</code>
</p><p>
No tak, ale to pole kwadratu, a nie sześcianu. Co z tym fantem
 zrobić?
</p>
<sect2>
Zastępowanie metod
<p>
Musimy poprawić metodę podaj_pole. Np. definiując ją zupełnie
 od zera:
</p>
<p>
<code>
class Szescian(Kwadrat):
  def podaj_pole(self):
    return (self.bok ** 2) * 6
</code>
</p><p>
Działa to znakomicie, ponieważ Python odróżnia obiekty i w zależności
 od obiektu właśnie wywoła prawidłową metodę:
</p>
<p>
<code>
kw = Kwadrat()
sz = Szescian()
kw.bok = sz.bok = 10 &num; jesteśmy już hackerami prawie pełną gębą, więc możemy pozwolić sobie na ekstrawagancję
print kw.podaj_pole(), sz.podaj_pole()
</code>
</p><p>
Działa? Działa! Ale można też inaczej.
</p>
<sect2>
Rozszerzanie metod
<p>
Wcześniej napisaliśmy od nowa kawałek odpowiedzialny za obliczanie
 pola. Ale moglibyśmy też w klasie Szescian wykorzystać to, co już
 napisaliśmy w klasie Kwadrat:
</p>
<p>
<code>
class Szescian(Kwadrat):
  def podaj_pole(self):
    return ???
</code>
</p><p>
No właśnie --- co "
return"
? Nie możemy napisać "
self.podaj_pole"
,
 bo wpadniemy w nieskończoną pułapkę. Ale możemy teraz użyć opisanego
 wcześniej nieeleganckiego sposobu wywoływania metod --- z podaną
 explicite klasą i obiektem jako argumentem:
</p>
<p>
<code>
    return Kwadrat.podaj_pole(self) * 6
</code>
</p><p>
I to jest to! Specyfikując klasę przed nazwą metody zmuszamy
 Pythona, aby wywołał metodę dokładnie wg zapisu w tej właśnie klasie.
 Ale klasa jest abstraktem, klasa nie pracuje! I dlatego podajemy
 jako argument "
self"
 --- jest to obiekt typu Szescian, ale dziedziczy
 on po Kwadracie. Czyli zmuszamy obiekt pochodny do udawania przez
 chwilę obiektu podstawowego --- albo inaczej --- zmuszamy klasę podstawową,
 aby jej metody przetworzyły obiekt pochodny. W tę stronę ta sztuczka
 się udaje, ponieważ klasa pochodna zawiera wszystkie składowe klasy
 podstawowej --- w drugą stronę jednak to nie przejdzie.
</p>
<sect2>
Wiązanie metod do obiektu
<p>
Python wiąże metody klasy z obiektem, do klasy którego należą
 --- niezależnie od miejsca wywołania. Jeśli metoda nie istnieje (a
 może), nastąpi przejście do klasy poprzednika. Ale tylko na czas
 wyszukiwania tej jednej metody. Przy następnym przeszukiwaniu proces
 znowu rozpocznie się od klasy obiektu. Popatrzmy:
</p>
<p>
<code>
class Kwadrat:
  bok = 0
  def wyswietl(self):
    print &quot;bok:&quot;, self.bok, &quot;; pole:&quot;, self.pole()
  def pole(self):
    return self.bok ** 2

class Szescian(Kwadrat):
  def pole(self):
    return Kwadrat.pole(self) * 6

sz = Szescian()
sz.bok = 2
sz.wyswietl()
</code>
</p><p>
Wynik działania -- 24, a więc prawidłowy. Jak to wszystko przebiegło?
 Interesuje nas punkt począwszy od wywołania metody "
wyswietl"
:
</p>
<p>
<enum>
 <item>
obiekt "
sz"
 jest klasy "
Szescian"
 i tutaj Python rozpocznie szukanie
 metody "
wyswietl"
,
 <item>
klasa "
Szescian"
 nie zawiera takiej metody, brany jest w takim
 przypadku poprzednik --- "
Kwadrat"
,
 <item>
ta klasa zawiera żądaną metodę --- generowane jest wywołanie
 "
Kwadrat.wyswietl(sz)"
,
 <item>
metoda "
wyswietl"
 klasy "
Kwadrat"
 wywołuje z kolei metodę "
pole"
,
 <item>
rzecz tyczy cały czas obiektu klasy "
Szescian"
 (mimo, iż obecnie
 znajdujemy się w obrębie klasy "
Kwadrat"
) --- przeszukiwana jest
 klasa "
Szescian"
 i metoda "
pole"
 zostaje znaleziona,
 <item>
metoda "
pole"
 klasy "
Szescian"
 z kolei wywołuje explicite metodę
 klasy "
Kwadrat"
,
 <item>
zostaje ona odnaleziona, wykonana i cały proces odwija się do
 wywołania, od którego zaczęliśmy.
</enum>
</p><sect1>
Konkretny przypadek
<p>
Wśród modułów dostarczanych razem z Pythonem znajduje się ConfigParser.
 Nie jest to moduł rewelacyjny, ale łatwo się z niego korzysta. Służy
 on do odczytu opcji z plików konfiguracyjnych (podzielonych na sekcje,
 każdy nagłówek sekcji znajduje się w nawiasach kwadratowych, każdej
 opcji przypisana jest jakaś wartość, która jest oddzielona od tej
 pierwszej znakiem '=' lub ':').)Dla naszych potrzeb trzeba będzie
 zmodyfikować nieco jedną linijkę, ponieważ wersja ortodoksyjna nie
 toleruje białych znaków w nagłówkach sekcji.
</p>
<p>
Dlatego w /usr/lib/python1.5/ znajdujemy plik ConfigParser.py
 i kopiujemy go do swojego katalogu, w którym eksperymentujemy. Na
 początku skryptu po linijce
</p>
<p>
<code>
&num;!/usr/bin/env python
</code>
</p><p>
dodajemy 
</p>
<p>
<code>
import ConfigParser 
</code>
</p><p>
W samym pliku zamieniamy (u mnie jest to linijka 327) kompilowane
 wyrażenie regularne __SECTCRE z 
</p>
<p>
<code>
...
r'(?P&lt;header&gt;&lsqb;-&bsol;w&rsqb;+)' 
...
</code>
</p><p>
na 
</p>
<p>
<code>
...
r'(?P&lt;header&gt;&lsqb;-&bsol;w &bsol;t&rsqb;+)'.
...
</code>
</p><p>
Dalsza część tekstu zakłada, że czasem używamy KDE. A to z tego
 powodu, że .kderc jest jedynym plikiem w moim katalogu domowym, który
 z grubsza przypomina modelowy plik konfiguracyjny (obecność sekcji
 itd.). 
</p>
<p>
W pliku ConfigParser jest zadeklarowana klasa ConfigParser --
 znajduje się tam również krótki opis metod tej klasy. Metody są funkcjami
 właściwymi dla danej klasy. Aby skorzystać z owych metod, musimy
 najpierw utworzyć obiekt należący do klasy ConfigParser (zadeklarowanej
 w pliku o tej samej nazwie -- stąd powtórzenie):
</p>
<p>
<code>
a = ConfigParser.ConfigParser()
</code>
</p><p>
Możemy teraz zobaczyć, jakie narzędzia mamy do dyspozycji:
</p>
<p>
<code>
&gt;&gt;&gt; dir(ConfigParser.ConfigParser)
&lsqb;'_ConfigParser__OPTCRE', '_ConfigParser__SECTCRE', '_ConfigParser__get', '_ConfigParser__read', '__doc__', '__init__', '__module__', 'add_section', 'defaults', 'get', 'getboolean', 'getfloat', 'getint', 'has_section', 'options', 'read', 'sections'&rsqb;
</code>
</p><p>
Najpierw wywołamy metodę read(), która wczyta plik konfiguracyjny:
</p>
<p>
<code>
&gt;&gt;&gt; a.read('.kderc')
</code>
</p><p>
Następnie sprawdźmy, jakie nagłówki zawiera nasz plik .kderc:
</p>
<p>
<code>
&gt;&gt;&gt; print a.sections()
&lsqb;'General', 'KDE', 'Standard Keys', 'WM', 'KFileDialogSettings', 'Locale'&rsqb;
</code>
</p><p>
Aby sprawdzić wartości, używamy metody get(). Sprawdźmy, jak
 się jej używa:
</p>
<p>
<code>
&gt;&gt;&gt; print a.get.__doc__
Get an option value for a given section.
 
        All &percnt; interpolations are expanded in the return values, based on the
        defaults passed into the constructor, unless the optional argument
        `raw' is true.  Additional substitutions may be provided using the
        `vars' argument, which must be a dictionary whose contents overrides
        any pre-existing defaults.
 
        The section DEFAULT is special.
</code>
</p><p>
Spróbujemy więc odczytać wartość opcji "
Next"
 z sekcji "
WM"
:
</p>
<p>
<code>
&gt;&gt;&gt; print a.get(&quot;WM&quot;,&quot;Next&quot;, raw=0, vars=None)
PageDown
</code>
</p><sect>
Wyjątki --- bezpieczne programowanie
<p>
"
Wyjątki"
 to skrót myślowy od "
sytuacje wyjątkowe"
, a mniej eufemistycznie
 --- błąd w wykonywaniu (a nie prekompilowaniu) programu. Wyjątki
 to mechanizm nie tyle zapobieganiu groźnym sytuacjom, ale sposób
 ich zgrabnego opanowania bez palpitacji serca u użytkownika.
</p>
<p>
Zanim zaczniemy opisywać głębiej sam temat, parę słów pochwały
 --- mechanizm wyjątków autentycznie zmienił oblicze programowania.
 Dzięki niemu można zapisać kod w sposób klarowniejszy, bardziej usystematyzowany,
 a w efekcie użytkownik powinien otrzymać oprogramowanie dużo stabilniejsze.
 Bez mechanizmu wyjątków to samo było oczywiście możliwe po stronie
 użytkownika, ale programista musiał nieźle zużyć klawiaturę, zanim
 doszedł do tego samego, o czym za chwilę opowiemy.
</p>
<sect1>
Prehistoria
<p>
Popatrzmy na banalny kod:
</p>
<p>
<code>
x = 10
y = 0
z = x / y
</code>
</p><p>
Wykonanie takiego programu się nie powiedzie, to rzecz jasna
 --- możemy przeciwdziałać błędom np. tak:
</p>
<p>
<code>
if y == 0:
  print &quot;Dzielnik równy zeru; niepoprawne dane&quot;
else:
  z = x / y
</code>
</p><p>
Tutaj nawet nie za bardzo widać ewentualnej potrzeby korzystania
 z wyjątków, w końcu te dwie linijki więcej... Ale jeśli danych jest
 więcej --- np. zbieramy od użytkownika informacje potrzebne do zapisu
 do bazy danych --- to chciałoby się nam bawić w dziesiątki "
if"
?
 Raczej nie. O wiele wygodniej puścić program na żywioł --- a niech
 sobie tam dzieli co chce, konwertuje ile wlezie --- a jeśli wystąpi
 błąd obsłużymy taką sytuację zbiorczo. Zapiszmy to schematycznie
 tak:
</p>
<p>
<code>
zbierz_dane()
blad = interpretuj_dane()
if blad:
  print &quot;Wprowadziłeś niepoprawne dane&quot;
zapisz_dane()
</code>
</p><sect1>
Konstrukcje obsługi wyjątków
<p>
Na marginesie: jeśli znacie Object Pascala, poczujecie się jak
 w domu --- nazewnictwo i konwencja działania jest prawie identyczna.
</p>
<sect2>
try-except-else (przechwytywanie definitywne)
<p>
<code>
try:
  ...  &num; kod programu
except:
  ...  &num; kod wykonywany w przypadku błędu
</code>
</p><p>
Python wykonuje kod zawarty między klauzulami &quot;try-except&quot;
 i jeśli zdarzy się tu błąd wykonywanie przeskoczy od razu do bloku
 "
except"
. Dlaczego jest to przechwycenie definitywne --- ewentualny
 błąd nie wycieka poza blok "
except"
, jest tam przechwytywany i zabijany.
</p>
<p>
Oto nowa wersje dzielenia:
</p>
<p>
<code>
try:
  z = x / y
except:
  print &quot;Niepoprawne dane&quot;
</code>
</p><p>
Dodatkowo możemy dopisać sekcję "
else"
 --- zostanie ona wykonana
 kiedy blok "
try-except"
 zostanie wykonany bez żadnego błędu:
</p>
<p>
<code>
try:
  z = x / y
except:
  print &quot;Niepoprawne dane&quot;
else:
  print &quot;Wynik:&quot;, z
</code>
</p><p>
Zwróć uwagę, że ostatni wiersz zostanie wykonany tylko i wyłącznie
 w przypadku braku błędu, gdybyś zapisał to tak:
</p>
<p>
<code>
try:
  z = x / y
except:
  print &quot;Niepoprawne dane&quot;

print &quot;Wynik:&quot;, z
</code>
</p><p>
To kod taki (wyświetlenie wyniku) zostałby wykonany w obu przypadkach!
</p>
<sect2>
try-finally (pośrednio i obligatoryjnie)
<p>
Konstrukcję tę zapisujemy analogicznie do "
try-except"
, ale jej
 działanie różni się w dwóch punktach:
</p>
<p>
<enum>
 <item>
sekcja "
finally"
 będzie wykonana zawsze! Jeśli błąd wystąpi,
 to zaraz po jego wystąpieniu, jeśli nie --- po ostatniej linii "
try"

 zostanie wykonana pierwsza linia "
finally"

 <item>
sekcja "
finally"
 przekazuje ewentualne błędy dalej --- propagacja
 nie zostaje zatrzymana
</enum>
<p>
<code>
&num; y określamy wcześniej
x = 10
try:
  z = x / y
finally:
  print &quot;koniec&quot;
</code>
</p><p>
Łatwo się przekonamy, że dla "
y"
 równego zero, zobaczymy napis
 "
koniec"
, ale zobaczymy także, że Python się nas obraził przerywając
 program ("
propagacj nie zostaje zatrzymana"
 --- to się sprawdza),
 natomiast dla "
y"
 równego powiedzmy jeden, także zobaczymy napis
 (ale program będzie działał dalej).
</p>
<p>
W takim razie --- czy to ma sens? I owszem, bowiem "
try-except"

 ma zapobiegać wyciekaniu błędów, ma dawać użytkownikowi informacje,
 a "
try-finally"
 ma pomagać programiście w zrobieniu porządku w danych.
 Oto klasyczny przykład (w odcinkach):
</p>
<p>
<code>
try:
  plik = open(&quot;zuzia.txt&quot;,'r')
  s = plik.readline()
  print s
finally:
  plik.close()
</code>
</p><p>
Taki kod zabezpiecza nas całkiem ładnie na wypadek błędów podczas
 czytania pliku --- jeśli cokolwiek złego się wydarzy nastąpi przeskok
 do sekcji "
finally"
 i plik zostanie zamknięty. Jeśli wszystko przebiegło
 zgodnie z planem --- sekcja "
finally"
 także zostanie wykonana, a
 plik zamknięty. Ale jest jeden szkopuł --- jeśli pliku w ogóle nie
 będzie, zmienna "
plik"
 będzie niezdefiniowana i w sekcji "
finally"

 wystąpi dodatkowy błąd! Możemy temu zaradzić dopisując:
</p>
<p>
<code>
plik = 0
try:
  ...
finally:
  if plik:
    plik.close()
</code>
</p><p>
Teraz lepiej --- jeśli plik nie zostanie otworzony, sprawdzimy
 to i nie będziemy go zamykać. Ale to nie wszystko --- błąd przecież
 się propaguje, przerywając program. Złu zaradzimy dodając blok "
try-except"

 --- oto kompletny kod:
</p>
<p>
<code>
plik = 0
try:
  try:
    plik = open(&quot;zuzia.txt&quot;,'r')
    s = plik.readline()
    print s
  finally:
    if plik:
      plik.close()
except:
  print &quot;Nastąpił błąd podczas czytania pliku&quot;
</code>
</p><p>
Zamknięcie pliku zostanie wykonane zawsze (oprócz sytuacji, kiedy
 dany plik w ogóle nie istnieje), także wtedy, kiedy wszystko przebiegło
 bez przeszkód, natomiast komunikat użytkownik zobaczy tylko wtedy,
 kiedy faktycznie wystąpił błąd --- czy to błąd otwarcia, czy czytania.
 Program jako taki nie zostanie przerwany.
</p>
<p>
I o to chodzi, i to chodzi.
</p>
<sect2>
Tajniki "
try-finally"

<p>
"
Finally"
 w Pythonie naprawdę znaczy, to co znaczy --- akcję
 finałową. Ponieważ jak już wiemy, sekcja ta dotyczy nie tylko sytuacji
 wyjątkowych, ale obejmuje ogół (wykonywana jest zawsze) --- ma ona
 wpływ także na klasyczne polecenia:
</p>
<p>
<code>
def pomnoz(x,y):
  try:
    return x*y
  finally:
    print &quot;Wynik:&quot;,
 
print pomnoz(3,4)
</code>
</p><p>
Jeśli spodziewacie się zobaczenia napisu "
Wynik"
 to intuicja
 Was nie zwodzi --- rzeczywiście, mimo, iż polecenie "
return"
 natychmiast
 opuszcza funkcje, to musi po drodze odwiedzić sekcję "
finally"
 ---
 "
finally"
 ma znaczenie nadrzędne!
</p>
<sect2>
Rozszerzone "
except"

<p>
"
Except"
 oprócz budowy takiej, jaką poznaliśmy do tej pory pozwala
 także nam na rozróżnianie typów błędów wraz z towarzyszącym komunikatem.
 Wróćmy do naszego przykładu z dzieleniem przez zero:
</p>
<p>
<code>
x = 10
y = 0
try:
  z = x / y
except ZeroDivisionError, komunikat:
  print komunikat
</code>
</p><p>
Przechwytujemy nie jakiś tam wyjątek, ale dokładnie określony
 --- w rzeczywistych jednak programach, lepiej nie pozostawiać dziur
 na sytuacje inne niż przewidzieliśmy pisząc program. Rozsądnie jest
 dopisać jeszcze dwa wiersze:
</p>
<p>
<code>
except:
  print &quot;Inny wyjątek&quot;
</code>
</p><p>
Najczęściej jednak będziemy łączyć taki zapis i korzystać z wyjątku
 ogólnego typu (tj. klasy podstawowej wyjątków, ponieważ wyjątki to
 klasy):
</p>
<p>
<code>
try:
  z = x / y
except Exception, komunikat:
  print komunikat
</code>
</p><sect1>
Generowanie wyjątków
<sect2>
raise (&quot;...i powstał wyjątek&quot;)
<p>
Wywoływanie wyjątków daje nam znakomitą możliwość zintegrowania
 się z wewnętrznymi mechanizmami Pythona i ujednolicenie programu,
 jeśli chodzi o komunikację z użytkownikiem i zapis samego kodu.
</p>
<p>
Przykład może nie najambitniejszy:
</p>
<p>
<code>
plik = 0

try:
  plik = open(&quot;dni.txt&quot;,'r')
  while 1:
    s = plik.readline()
    if not s:
      break
    dzien = eval(s&lsqb;:-1&rsqb;) &num; pomijamy znak nowego wiersza
    if not (1 &lt;= dzien &lt;= 31):
    raise Exception, &quot;dzień spoza zakresu&quot;

except Exception, komunikat:
  print &quot;Błąd:&quot;, komunikat 

if plik:
  plik.close()
</code>
</p><p>
Polecenie &quot;raise&quot;, bo ono jest tu nowinką, generuje
 wyjątek typu podanego w pierwszym parametrze, z komunikatem określonym
 w drugim (nieobowiązkowym) parametrze.
</p>
<p>
Dzięki takiemu zapisowi mamy spójny kod, który zabezpiecza nas
 przed brakiem pliku, błędami natury technicznej w odczycie, niepoprawnymi
 danymi w samej treści -- użytkownik dostaje komunikat, który coś
 znaczy, a przy tym program nie przerywa działania i sprząta po sobie
 (zamknięcie otwartego pliku).
</p>
<sect2>
Własne typy wyjątków
<p>
Jeżeli chcemy dokładnie usystematyzować wyjątki, możemy zamiast
 posługiwać się ogólnym typem, zdefiniować swój własny --- należy
 tylko pamiętać, że podstawowym typem wszystkich wyjątków powinna
 być klasa &quot;Exception&quot;:
</p>
<p>
<code>
class ExceptionNiepoprawnyDzien(Exception):
  pass

...
    if not (1 &lt;= dzien &lt;= 31):
      raise ExceptionNiepoprawnyDzien, &quot;dzień spoza zakresu&quot;
... 
</code>
</p><p>
Oczywiście i w tym przypadku parametr komunikatu moglibyśmy opuścić.
</p>
<p>
Dlaczego nie możemy definiować własnych wyjątków na przykład
 tak
</p>
<p>
<code>
MojWyjatek = &quot;mój wyjątek&quot; &num; tekstowy wyróżnik wyjątku

try:
  raise MojWyjatek
except MojWyjatek:
  print &quot;Błąd&quot;
</code>
</p><p>
...tym bardziej, jeśli to działa? No tak --- działa --- ale do
 czasu. Jeśli chcielibyśmy reagować na wyjątki w sposób ogólny:
</p>
<p>
<code>
except Exception:
  print &quot;Błąd&quot;
</code>
</p><p>
to nasz własny wyjątek prześlizgnie się przez sieci --- nie należy
 on do klasy (tj. do drzewa klas) typu &quot;Exception&quot;.
</p>
<p>
Podsumowując --- zapomnij o tym sposobie!
</p>
<sect2>
Raise --- wyjątki w sztafecie
<p>
Jeżeli to potrzebne możemy z sekcji &quot;except&quot; wywołać
 wyjątek właśnie przechwycony albo inaczej mówiąc --- pozwolić mu
 przejść dalej, tak jak to dzieje się w sekcji &quot;finally&quot;.
 Ponieważ jest to dokładnie ten sam wyjątek, który złapaliśmy nie
 podajemy żadnych parametrów:
</p>
<p>
<code>
try:
  z = x / y
except:
  print &quot;Błąd dzielenia&quot;
  raise
</code>
</p><p>
Czy wygodniejsze jest użycie &quot;finally&quot;, czy &quot;except-raise&quot;
 zależy od programisty i konkretnej sytuacji --- Python w każdym razie
 pozostawia szerokie pole manewru.
</p>
<sect>
Czyżby koniec?
<p>
Chętni zainteresowani rozwinięciem materiału o Pythonie proszeni
 są o kontakt ;-)
</p>


</article>
