![]() |
|
Programowanie:
Artykuły
FAQ
Download
Książki
WWW:
Artykuły
Narzędzia
Kursy
Darmowe
FAQ
Skrypty
Humor
Inne:
Forum
Wiki
Liczniki
Linki
Chat
Grafika
Video
Inne
|
|
Autor: Artur Skura (arturs [bzium] linuxpl.org)
podziękowania dla Wojtka Warczakowskiego (wowar [bzium] poczta.onet.pl) gdzieniegdzie tekst modyfikował Maciej " MACiAS" Pilichowski (macias [bzium] torun.pdi.net) 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ą ;-) 1. Wstę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ć. 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. 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. 2. Zaczynamy Aby zorientować się w możliwościach języka, uruchomimy interpreter Pythona: $ python Python 1.5.2 (#1, Feb 1 2000, 16:32:16) [GCC egcs-2.91.66 19990314/Linux (egcs- on linux-i386 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> >>> print "Mamusiu, jak tu pięknie!" 2.1 Wstępnych uwag parę Zaczynamy gubić przykłady 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: python tescik.py Jak łatwo zauważyć... print x[zk]*f/u[ol],we[d[m]]*r # o rrrany, w ogóle nie wiem co się tutaj dziejeA bo mnie się shift nacisnął... Jeszcze nie wiemy, jak Python działa, ale już na wstępie: >>> Q = 100 >>> print q 3. Wbudowane typy danych 3.1 Łańcuchy 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. Przypiszmy teraz zmiennej " a" napis " Dajcie mi kredki" : >>> a = "Dajcie mi kredki." >>> print a Dajcie mi kredki. >>> a = "Dajcie" >>> b = "mi w końcu te kredki!!!" >>> print a, b Dajcie mi w końcu te kredki!!! >>> a = "No, " >>> b = "wreszcie!" >>> print a+b No, wreszcie! >>> print "Jarek powiedział:", a + b Jarek powiedział: No, Wreszcie! >>> s = "Nie!" >>> print 3*s Nie!Nie!Nie! >>> x = "7" >>> print x*3 777 >>> x = eval(x) # obliczenie wartości >>> print x*3 21 x = `x` # odwrotne apostrofy, to samo co " eval" >>> s = "lajkonik" >>> print s[0] l >>> print s[1] a Łańcuch [:2] odnosi się do pierwszych dwóch znaków łańcucha (czyli znaków o indeksie 0 i 1), zaś łańcuch [2:] -- do wszystkiego oprócz pierwszych dwóch znaków: >>> print s[2:] jkonik >>> print s[:2] la >>> print s[1:5] ajko >>> print s[:len(s)-1] lajkoni >>> print s[:-1] lajkoni Pisanie pod lupą Powyżej używaliśmy polecenia print z rozmaitymi operatorami. Teraz opiszemy je nieco dokładniej. print "ala ma kota" print "ala ma kota", print "ala", "ma","kota" print "ala"+"ma"+"kota" 3.2 Liczby Operacje na liczbach również odbywają się w sposób intuicyjny, Rozważmy prosty przykład: >>> a = 10 >>> b = 10 >>> print a, b 10 10 >>> print a+b, a-b, a*b, a/b 20 0 100 1 >>> round(10.5) 11.0 >>> pow(5,2) 25 >>> 5 ** 2 25 >>> 4|1, ~4, 4>>1 (5, -5, 2) >>> 4 or 2, 5 and 3 (4, 3) >>> hex(10), oct(10) ('0xa', '012') >>> x = 10 >>> x += 5 >>> print x 15 X operator= ... X = X operator (...) 3.3 Listy Listy przypominają nieco tablice w innych językach. Charakteryzują się umieszczeniem elementów w nawiasach kwadratowych. Rozważmy prosty przykład: >>> lista = ["Polak", "Rosjanin", "Niemiec"] >>> print lista ['Polak', 'Rosjanin', 'Niemiec'] >>> dir(lista) ['append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> lista.append("Maciej") >>> print lista ['Polak', 'Rosjanin', 'Niemiec', 'Maciej'] >>> lista.sort() >>> print lista ['Maciej', 'Niemiec', 'Polak', 'Rosjanin'] >>> print lista.append.__doc__ L.append(object) -- append object to end >>> print lista ['Tytus', 'Romek', 'Atomek'] >>> lista.insert.__doc__ 'L.insert(index, object) -- insert object before index' >>> lista.insert(1, "Szarik") >>> print lista ['Tytus', 'Szarik', 'Romek', 'Atomek'] 3.4 Zbiory (tuple) i słowniki 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ą. Zbiór to typ danych bardzo przypominający listę, wizualnie różni się brakiem nawiasów. >>> kolejka = 'Jacek', 'Wacek', 'Pankracek' >>> print kolejka ('Jacek', 'Wacek', 'Pankracek') >>> print kolejka[1] Wacek >>> kolejka[1] = 'Agatka' Traceback (innermost last): File "<stdin>", line 1, in ? TypeError: object doesn't support item assignment >>> slownik = { 'Ania':96, 'Gosia':69} >>> slownik['Ania'] 96 >>> dir(slownik) ['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'update', 'values'] >>> print slownik.keys() ['Ania', 'Gosia'] >>> print slownik.values() [96, 69] >>> print slownik.items()[1] ('Gosia', 69) ...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 ({}, BEGIN..END) z innych języków. 4.1 Konstrukcje warunkowe if (jeśli) >>> if 2+2==4: ... print "2+2=4!" ... 2+2=4! elif ("jeśli natomiast...") >>> if 2+2==5: ... print "tego raczej nie wydrukujemy" ... elif 2+2==4: ... print "a to właśnie powinniśmy zobaczyć" >>> if 2+2==4: ... print "Murphy powiedziałby..." ... else: ... print "...że ten napis zostanie wyswietlony" Ponownie warto zwrócić uwagę na wcięcia. 4.2 Konstrukcje iteracyjne for ("dla każdego x z przedziału...") For jest zaimplementowana w Pythonie w nieco specyficzny sposób: >>> lista = ['Tytus', 'Romek', 'Atomek'] >>> for i in lista: ... print i ... Tytus Romek Atomek >>> print range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print range(5,10) [5, 6, 7, 8, 9] >>> for i in range (1,4): ... print "Jan Sobieski miał trzy pieski:", i ... Jan Sobieski miał trzy pieski: 1 Jan Sobieski miał trzy pieski: 2 Jan Sobieski miał trzy pieski: 3 >>> while a == 1: ... print "OK" a = 1 while a < 10: a += 1 ; print a else: print "koniec" a = 1 while a < 10: a += 1 ; print a print "koniec" Konstrukcje for i while określa się żargonowo pętlami --- mamy pętlę for i pętlę while. break Polecenie to przydaje się, kiedy chcemy natychmiast przerwać wykonywanie pętli, np.: for i in range(100): if i==50: break print i for i in range(100): while i<k: wynik = wynik + (i*k) k = k - 1 if i==50: break print wynik for i in range(100): while i<k: wynik = wynik + (i*k) if i==50: break k = k -1 print wynik Należy podkreślić, iż konstrukcja "while-else" stanowi całość i polecenia " break" opuszcza całą konstrukcję --- " else" nie zostanie wykonane. continue 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. pass Czyli "nic nie rób" . Nieraz przydatne. 4.3 Tajniki kontroli programu Porównania 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 " <>" (tak jak w Pascalu) bądź jako " !=" (jak w C). Mamy też do dyspozycji operatory nierówności: " <" , " >" , " <=" , " >=" , a co więcej -- możemy je łączyć, np.: if 10<=x<=100: ... Python dokonuje skróconego sprawdzania warunków --- jeśli w pewnym momencie okaże się, że wynik jest już ustalony, sprawdzanie jest przerywane. Np: >>> x = 4 >>> y = 5 >>> if (x == 4) or (y == 777): ... print "a jednak" 4.4 Ułożenie kodu programu Wcięcia 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ę). 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. Jednolinijkowce Do tej pory widziałeś tak zapisany program: x = 4 y = 10 z = 13 x = 4 ; y = 10 ; z = 13 x = 20 y = 20 x = y = 20 for i in range(100): print i 5.1 Podstawy 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. >>> def dodaj(a,b): ... return a+b ... >>> dodaj(1,2) 3 >>> def zamien(a,b): ... temp=a ... a=b ... b=temp ... return a,b ... >>> zamien(10,11) (11, 10) Argumenty domyślne Funkcjom możemy przypisać domyślne argumenty: >>> def pytanie(odpowiedz="Tito"): ... return odpowiedz ... >>> pytanie('Lenin') 'Lenin' >>> pytanie() 'Tito' Python pozwala na bardzo eleganckie przypisywanie wartości pewnym argumentom. Powiedzmy, że mamy taką oto funkcję: >>> def okno(x=0,y=0,szerokosc=400,wysokosc=200,ramka=1,kolor=kGranatowy,czcionka=Times) >>> okno(czcionka=Arial) 5.3 Dane proste i wskazywane Popatrzmy na poniższy przykład: >>> def wypelnij_liste(lista): ... lista = ["czyzby","kotek","zginal"] ... >>> lista = ["ala","ma","kotka"] >>> wypelnij_liste(lista) >>> print lista lista = ["ala","ma","kotka"] 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. Oczywiście z tego samego powodu w poniższym przykładzie zmieni się nam właściciel futrzaka: >>> def wypelnij_liste(lista): ... lista[0] = "tomek" ... >>> lista = ["ala","ma","kotka"] >>> wypelnij_liste(lista) >>> print lista >>> def wypelnij_liste(lista_arg): ... lista = lista_arg[:] # tutaj tworzymy <bf>kopię argumentu ... lista[0] = "tomek" ... >>> lista = ["ala","ma","kotka"] >>> wypelnij_liste(lista) >>> print lista 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). 5.4 Zasięg nazw 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: tytul = "ala" def zmien(): tytul = "zuzia" # ten wiersz będziemy omawiać print "funkcja", tytul zmien() print "program", tytul def zmien(): global tytul def zmien(): podtytul = tytul + " ma kota" print "funkcja", podtytul def zmien(): tytul = tytul + " ma kota" print "funkcja", tytul 6. Moduły 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. 6.1 string -- łańcuchy znaków Moduł string dostarcza wielu funkcji do manipulacji łańcuchami znaków. Zaimportujmy go: >>> import string >>> dir(string) ['__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'] >>> from string import upper >>> from string import * >>> a = "Kiedy byłem mały" >>> print upper(a) KIEDY BYŁEM MAŁY >>> print capitalize(a) Kiedy byłem mały >>> print capwords(a) Kiedy Byłem Mały >>> b=split(a) >>> print b[0], b[2] Kiedy mały >>> find(a,'y') 4 >>> count(a,'y') 3 Wyrażenia regularne pozwalają na odnalezienie znaków pasujących do podanego wzorca. >>> import re >>> re.findall("a", 'Jacek Klucha') ['a', 'a'] >>> re.findall("a.", 'Jacek Klucha') ['ac'] >>> re.findall("^J", 'Jacek Klucha') ['J'] >>> re.findall("cha$", 'Jacek Klucha') ['cha'] >>> re.findall("a[a-d]e", 'Jacek Klucha') ['ace'] >>> re.split("m", "Mamma mia") ['Ma', '', 'a ', 'ia'] >>> import regex >>> regex.match('a', 'ala') 1 >>> regex.match('b', 'ala') -1 urllib to bardzo przydatna biblioteka do ściągania stron WWW. Ale nie tylko. Rozpatrzmy prosty przykład: ściągnięcie strony www.tpnet.pl. >>> from urllib import * >>> link = URLopener().open('http://www.tpnet.pl') >>> print link <addinfourl at 135299432 whose fp = <open file '<socket>', mode 'rb' at 80b3b20>> >>> print link.readlines() Ale to nie wszystko. Obiekt link posiada bowiem również metodę info(), pozwalającą uzyskać informacje o połączeniu: >>> 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: "2983e-1a16-38fece26" Accept-Ranges: bytes Content-Length: 6678 Connection: close Content-Type: text/html >>> print link.info().getheader('Server') Apache/1.3.9 (Unix) #!/usr/bin/env python from urllib import * import re a = URLopener().open("http://linux.cgs.pl/linuxpl/2000-07/index.html") #można też urllib.urlopen() b = a.read() c = re.findall(r'\[linuxpl\].*.</STRONG',b) for i in range(len(c)): d = c[i] print d[9:(len(d)-8)] 7.1 Klasy i obiekty 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. 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 jak zbudować obiekt, ale to nie klasa "działa" a obiekt. Mówiąc krótko --- klasa jest abstraktem, a obiekt jej (klasy) instancją. Metody i pola 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: #!/usr/bin/env python # Powyżej standardowy początek skryptów w Pythonie w systemach typu Unix # definiujemy klasę Kwadrat: class Kwadrat: bok = 0 # do tego metodę podającą wartość boku def podaj_bok(self): return self.bok 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. A jak to działa? kw = Kwadrat() kw.bok = 14 print kw.podaj_bok() print Kwadrat.podaj_bok(kw) Obiekty są od siebie niezależne, to tylko różne instancje tej samej klasy. Popatrzmy na poniższy kod: a = Kwadrat() b = Kwadrat() a.bok = 14 b.bok = 300 print a.podaj_bok(), b.podaj_bok() Pułapki klas --- inicjowanie pól klasy 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: class Kwadrat: bok = 0 kw = Kwadrat() class KlasaListy: lista = [] a = KlasaListy() b = KlasaListy() # dołączenie, wskazanie pozostaje niezmienione a.lista.append("zuzia") print a.lista, b.lista a = KlasaListy() b = KlasaListy() # określenie nowych wskazań a.lista = ["zuzia"] b.lista = ["kotek"] print a.lista, b.lista 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ń. class KlasaListy: # zupełne pominięcie <bf>inicjowania pól klasy def kopiuj(self,l): self.lista = l[:] a = KlasaListy() a.kopiuj(["ala"]) print a.lista 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: a = KlasaListy() # obiekt o nazwie "a" rozpoczyna życie class KlasaListy: def __init__(self): print "hello, właśnie się narodziłem" a = KlasaListy() # niejawne wywołanie metody " __init__" 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ć. class KlasaListy: def pokaz_liste(ten_obiekt): print ten_obiekt.lista 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. Dodajmy do naszej klasy Kwadrat metodę obliczającą pole: def podaj_pole(self): return self.bok ** 2 class Szescian(Kwadrat): .... passa = Szescian() a.bok = 10 print a.podaj_pole() Zastępowanie metod Musimy poprawić metodę podaj_pole. Np. definiując ją zupełnie od zera: class Szescian(Kwadrat): def podaj_pole(self): return (self.bok ** 2) * 6 kw = Kwadrat() sz = Szescian() kw.bok = sz.bok = 10 # jesteśmy już hackerami prawie pełną gębą, więc możemy pozwolić sobie na ekstrawagancję print kw.podaj_pole(), sz.podaj_pole() Rozszerzanie metod 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: class Szescian(Kwadrat): def podaj_pole(self): return ??? return Kwadrat.podaj_pole(self) * 6 Wiązanie metod do obiektu 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: class Kwadrat: bok = 0 def wyswietl(self): print "bok:", self.bok, "; pole:", 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() 1. obiekt " sz" jest klasy " Szescian" i tutaj Python rozpocznie szukanie metody " wyswietl" , 2. klasa " Szescian" nie zawiera takiej metody, brany jest w takim przypadku poprzednik --- " Kwadrat" , 3. ta klasa zawiera żądaną metodę --- generowane jest wywołanie " Kwadrat.wyswietl(sz)" , 4. metoda " wyswietl" klasy " Kwadrat" wywołuje z kolei metodę " pole" , 5. 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, 6. metoda " pole" klasy " Szescian" z kolei wywołuje explicite metodę klasy " Kwadrat" , 7. zostaje ona odnaleziona, wykonana i cały proces odwija się do wywołania, od którego zaczęliśmy. 7.3 Konkretny przypadek 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. 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 #!/usr/bin/env pythonimport ConfigParser ... r'(?P<header>[-\w]+)' ... ... r'(?P<header>[-\w \t]+)'. ... 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): a = ConfigParser.ConfigParser() >>> dir(ConfigParser.ConfigParser) ['_ConfigParser__OPTCRE', '_ConfigParser__SECTCRE', '_ConfigParser__get', '_ConfigParser__read', '__doc__', '__init__', '__module__', 'add_section', 'defaults', 'get', 'getboolean', 'getfloat', 'getint', 'has_section', 'options', 'read', 'sections'] >>> a.read('.kderc') >>> print a.sections() ['General', 'KDE', 'Standard Keys', 'WM', 'KFileDialogSettings', 'Locale'] >>> print a.get.__doc__ Get an option value for a given section. All % 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. >>> print a.get("WM","Next", raw=0, vars=None) PageDown 8. Wyjątki --- bezpieczne programowanie "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. 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. 8.1 Prehistoria Popatrzmy na banalny kod: x = 10 y = 0 z = x / y if y == 0: print "Dzielnik równy zeru; niepoprawne dane" else: z = x / y zbierz_dane() blad = interpretuj_dane() if blad: print "Wprowadziłeś niepoprawne dane" zapisz_dane() Na marginesie: jeśli znacie Object Pascala, poczujecie się jak w domu --- nazewnictwo i konwencja działania jest prawie identyczna. try-except-else (przechwytywanie definitywne) try: ... # kod programu except: ... # kod wykonywany w przypadku błędu Oto nowa wersje dzielenia: try: z = x / y except: print "Niepoprawne dane" try: z = x / y except: print "Niepoprawne dane" else: print "Wynik:", z try: z = x / y except: print "Niepoprawne dane" print "Wynik:", z try-finally (pośrednio i obligatoryjnie) Konstrukcję tę zapisujemy analogicznie do " try-except" , ale jej działanie różni się w dwóch punktach: 1. 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" 2. sekcja " finally" przekazuje ewentualne błędy dalej --- propagacja nie zostaje zatrzymana # y określamy wcześniej x = 10 try: z = x / y finally: print "koniec" 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): try: plik = open("zuzia.txt",'r') s = plik.readline() print s finally: plik.close() plik = 0 try: ... finally: if plik: plik.close() --- oto kompletny kod: plik = 0 try: try: plik = open("zuzia.txt",'r') s = plik.readline() print s finally: if plik: plik.close() except: print "Nastąpił błąd podczas czytania pliku" I o to chodzi, i to chodzi. Tajniki "try-finally" "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: def pomnoz(x,y): try: return x*y finally: print "Wynik:", print pomnoz(3,4) Rozszerzone "except" "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: x = 10 y = 0 try: z = x / y except ZeroDivisionError, komunikat: print komunikat except: print "Inny wyjątek" try: z = x / y except Exception, komunikat: print komunikat 8.3 Generowanie wyjątków raise ("...i powstał wyjątek") Przykład może nie najambitniejszy: plik = 0 try: plik = open("dni.txt",'r') while 1: s = plik.readline() if not s: break dzien = eval(s[:-1]) # pomijamy znak nowego wiersza if not (1 <= dzien <= 31): raise Exception, "dzień spoza zakresu" except Exception, komunikat: print "Błąd:", komunikat if plik: plik.close() 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). Własne typy wyjątków 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 "Exception": class ExceptionNiepoprawnyDzien(Exception): pass ... if not (1 <= dzien <= 31): raise ExceptionNiepoprawnyDzien, "dzień spoza zakresu" ... Dlaczego nie możemy definiować własnych wyjątków na przykład tak MojWyjatek = "mój wyjątek" # tekstowy wyróżnik wyjątku try: raise MojWyjatek except MojWyjatek: print "Błąd" except Exception: print "Błąd" Podsumowując --- zapomnij o tym sposobie! Raise --- wyjątki w sztafecie Jeżeli to potrzebne możemy z sekcji "except" wywołać wyjątek właśnie przechwycony albo inaczej mówiąc --- pozwolić mu przejść dalej, tak jak to dzieje się w sekcji "finally". Ponieważ jest to dokładnie ten sam wyjątek, który złapaliśmy nie podajemy żadnych parametrów: try: z = x / y except: print "Błąd dzielenia" raise 9. Czyżby koniec? Chętni zainteresowani rozwinięciem materiału o Pythonie proszeni są o kontakt ;-)
| ||||||||||||||||||||||||||||||||||||
|