1 Pętla while JEZYK PROGRAMOWANIA PYTHON: PETLE ITERUJACE, KORZYSTANIE Z PLIKÓW E. Dyguda-Kazimierowicz Instrukcja while, wraz z omówioną poniżej instrukcją for, należą do pętli iterujacych pozwalających na wielokrotne uruchamianie bloku kodu, odpowiednio do momentu, aż dany warunek stanie się fałszywy (pętla while) lub określoną ilość razy (petla for). Podobnie jak instrukcja warunkowa, while i for są instrukcjami złożonymi, zawierającymi wcięte instrukcje (blok) poprzedzone wierszem nagłówkowym. Reguły składni są tu takie same, jak dla poznanej już instrukcji warunkowej: >>> while warunek: #wiersz naglowkowy z dwukropkiem instrukcja1 #poczatek bloku instrukcji instrukcja2 instrukcja3 #koniec bloku instrukcji Blok instrukcji zawarty w pętli while powtarzany jest tak długo, jak długo warunek w wierszu nagłówkowym jest prawdziwy. Po każdorazowym wykonaniu bloku instrukcji, sterowanie wraca do wiersza nagłówkowego. Test prawdziwości warunku wykonywany jest przed wejściem do bloku instrukcji. Jeśli wartość logiczna warunku to od samego początku fałsz, blok instrukcji objęty pętlą zostanie pominięty. Analogicznie zawsze prawdziwy warunek równoznaczny jest z nieskończonym działaniem pętli (o praktycznym wykorzystaniu takiej sytuacji za chwilę). Zazwyczaj w bloku pętli pojawiają się zatem instrukcję modyfikujące warunek. W poniższym przykładzie (obliczanie silni liczby n), zmienna n sterująca działaniem pętli zmniejszana jest w każdym kroku o 1, a sama pętla przerywana jest, gdy n przyjmie wartość 0: 1
2 PETLA FOR >>> n = 7 >>> wynik = 1 >>> while n: wynik *= n n -= 1 >>> print wynik 5040 Wypisanie łańcucha za każdym razem skracanego o pierwszy znak również zrealizować można za pomocą pętli while: >>> napis = iteracja >>> while napis: print napis napis = napis[1:] iteracja teracja eracja #itd 2 Pętla for Instrukcja for przeprowadza iterację na obiektach dowolnej sekwencji (łańcucha, krotki bądź listy). W każdym kroku pętli licznik przyjmuje wartość kolejnej pozycji sekwencji i następuje wykonanie bloku instrukcji; powtarzanie pętli kończy się w momencie osiągnięcia końca sekwencji. Liczba powtórzeń pętli wynosi zatem len(sekwencja). W przypadku pustej sekwencji blok instrukcji zostanie pominięty. Ogólna postać pętli for jest analogiczna do poznanych już instrukcji if i while (różnice dotyczą tylko postaci wiersza nagłówkowego): >>> for licznik in sekwencja: instrukcja1 #poczatek bloku instrukcji instrukcja2 instrukcja3 #koniec bloku instrukcji 2
2 PETLA FOR Najprostsze zastosowanie pętli for polega na wypisaniu kolejnych pozycji sekwencji. Po wyjściu z pętli zmienna będąca licznikiem (tutaj pozycja) nie jest usuwana ma wartość równą ostatniej pozycji: >>> napis = iteracja >>> lista = [0, napis, jeden, 3.5] >>> krotka = (3, -100, lista) >>> for pozycja in napis: print pozycja, #przecinek powyzej zapobiega przejsciu do nowej linii i t e r a c j a >>> for pozycja in lista: print pozycja, 0 iteracja jeden 3.5 >>> for pozycja in krotka: print pozycja, 3-100 [0, iteracja, jeden, 3.5] >>> pozycja [0, iteracja, jeden, 3.5] Podobny efekt można uzyskać przy pomocy pętli while, wymaga to jednak jawnego indeksowania (przykład poniżej). Pętla for automatycznie inicjuje indeks, określa jego zakres i pobiera za jego pomocą kolejne pozycje sekwencji, zwiększając wartość indeksu o 1. >>> pozycja = 0 >>> while pozycja < len(napis): print napis[pozycja], pozycja += 1 i t e r a c j a Jeżeli przeglądana sekwencja składa się z krotek o jednakowym rozmiarze, licznik również może być krotką (tego samego rozmiaru): 3
2 PETLA FOR >>> for (x, y) in [(1, 1), (-1, 2), (0, 3)]: print %4d %4d Suma: %4d % (x, y, x + y) 1 1 Suma: 2-1 2 Suma: 1 0 3 Suma: 3 Sekwencje zmienne (np. listy), na których dokonywana jest iteracja, nie powinny być modyfikowane wewnątrz pętli prowadzi to do pomijania bądź powtarzania wykonania bloku dla niektórych pozycji. Jeżeli taka modyfikacja jest konieczna, iterację można zastosować do kopii listy: >>> liczby = [1, 2, 7, 6, 5, 0, 4, 3] >>> for i in liczby: if i%2 == 0: liczby.remove(i) #usuwaj parzyste >>> liczby [1, 7, 5, 4, 3] #pozostala liczba 4! >>> liczby = [1, 2, 7, 6, 5, 0, 4, 3] >>> for i in liczby[:]: #iteracja na kopii listy if i%2 == 0: liczby.remove(i) #usuwaj parzyste >>> liczby [1, 7, 5, 3] W niektórych zadaniach przydatna jest możliwość ręcznego indeksowania (np. gdy zachodzi potrzeba wyświetlania nie tylko wartości odpowiadającej danej pozycji w sekwencji, ale również jej indeksu). Funkcja range() służy do generowania listy liczb całkowitych z przedziału zadanego jako dwa argumenty. Pojedynczy argument oznacza górną granicę zakresu (domyślnie dolną granicą jest 0); górna granica nie wchodzi skład wynikowej listy. Dodatkowo można podać trzeci argument określający skok wartości (domyślnie 1; ujemna wartość oznacza odliczanie w dół). >>> print range(3), range(3, 7), range(3, 7, 2), range(7, 3, -1) [0, 1, 2] [3, 4, 5, 6] [3, 5] [7, 6, 5, 4] Przykłady zastosowania list uzyskanych za pomocą funkcji range() do jawnego indeksowania sekwencji oraz generowania listy krotek zawierającej kwadraty i sześciany kolejnych liczb całkowitych podane są poniżej: 4
3 INSTRUKCJE BREAK I CONTINUE >>> pozycja = 0 >>> for pozycja in range(len(napis)): print %c na pozycji %d % (napis[pozycja], pozycja) i na pozycji 0 t na pozycji 1 e na pozycji 2 #itd >>> n = 10 >>> wyniki = [] >>> for i in range(n + 1): wyniki.append((i*i, i*i*i)) #dynamiczne budowanie listy >>> print wyniki[2], wyniki[7], wyniki[n] (4, 8) (49, 343) (100, 1000) 3 Instrukcje break i continue Pętla while sprawdza prawdziwość warunku na początku, podczas gdy w niektórych sytuacjach zachodzi konieczność conajmniej jednorazowego wykonania bloku instrukcji z pętli i w zależności od uzyskanego wyniku ewentualna kontynuacja lub przerwanie pętli. Przykładowo program pobierający dowolną ilość danych od użytkownika nie wie z góry, ile razy ma wywołać funkcję raw_input(). W takim przypadku w wierszu nagłówkowym umieszcza się dowolny, zawsze prawdziwy warunek, a w bloku instrukcji polecenie break, które powoduje wyjście z pętli (zazwyczaj w tym miejscu sprawdzany jest inny warunek i zależnie od jego wyniku uruchamiana jest instrukcja break). Przerwanie poniższej pętli (czyli wywołanie break) następuje po wprowadzeniu pustego łańcucha. Każda podana przed tym zdarzeniem wartość dołączana jest do listy dane: >>> dane = [] >>> while 1: x = raw_input( Podaj dane: ) if not x: break dane.append(x) 5
3 INSTRUKCJE BREAK I CONTINUE Podaj dane: 2 Podaj dane: 3 Podaj dane: >>> print dane [ 2, 3 ] Tak samo działa instrukcja break w przypadku pętli for: część iteracji jest pomijana i sterowanie wychodzi poza pętlę. W podanym poniżej przykładzie, pętla for zwraca pierwszą napotkaną pozycję listy dane, w której pojawia się znak nie będący cyfrą, zaraz po tym jej działanie zostaje przerwane: >>> dane = [2, 5, x, -3, 0.5, abc ] >>> for pozycja in dane: if not str(pozycja).isdigit(): print pozycja break x Zastąpienie powyższej instrukcji break przez continue spowodowałoby pominięcie dalszych instrukcji z bloku i przeskok na początek pętli: >>> for pozycja in dane: if not str(pozycja).isdigit(): print pozycja continue #pomin ponizsza instrukcje print Pozycja "%s" zawiera wylacznie cyfry % pozycja Pozycja "2" zawiera wylacznie cyfry Pozycja "5" zawiera wylacznie cyfry x -3 0.5 abc Wstawienie continue do pętli while również powoduje przejście sterowania do wiersza nagłówkowego następuje ponowne sprawdzenie warunku i w zależności od wyniku powtórzenie instrukcji zawartych w bloku lub wyjście poza pętlę. 6
3 INSTRUKCJE BREAK I CONTINUE W pętlach opcjonalnie stosować można klauzulę else. Zawarte w niej instrukcje wykonywane są po wyjściu z pętli (czyli zakończeniu iteracji for lub gdy warunek sterujący while okaże się fałszywy), pod warunkiem, że działanie pętli nie zostało przerwane instrukcją break: >>> dane = [231, 157, 963] >>> for pozycja in dane: if not str(pozycja).isdigit(): print pozycja break else: print Dane zawieraja wylacznie cyfry Dane zawieraja wylacznie cyfry Jeżeli blok instrukcji pętli nie zawiera break, stosowanie klauzuli else nie ma większego sensu objęte nią instrukcje będą wykonane tak samo, jak po umieszczeniu ich zwyczajnie jako kontynuację kodu, następującą po wyjściu z pętli. Pętle, jako instrukcje złożone, można dowolnie zagnieżdżać. Wstawienie break lub continue do pętli wewnętrznej (zagnieżdżonej) nie ma wpływu na działanie pętli zewnętrznej. Uruchomienie instrukcji break w poniższym kodzie (służącym do wyszukiwania wspólnych elementów dwóch list) powoduje wyjście tylko z wewnętrznej pętli for. Polecenie print zawarte w klauzuli else będzie uruchomione tylko w sytuacji, gdy nie zostało wcześniej wywołane break (czyli gdy nie znaleziono identycznej pozycji w drugiej liście); else odnosi się do pętli wewnętrznej, stąd jego wcięcie na taką samą głębokość, jak wiersza nagłówkowego drugiej instrukcji for: >>> lista1 = [11, Q, 0.0] >>> lista2 = [111, q, Q, 11 ] >>> for i in lista1: for j in lista2: if i == j: print Wspolny element:, i break else: print i, nie wystepuje w obu listach 11 nie wystepuje w obu listach Wspolny element: 0.0 nie wystepuje w obu listach Q 7
4 PODSTAWOWE OPERACJE NA PLIKACH 4 Podstawowe operacje na plikach Użyteczność większości programów polega na możliwości pobierania wielu danych, fizycznie zgromadzonych w plikach, ich ewentualnym przetwarzaniu i/lub zapisywaniu wyników działania w innych plikach. Pliki w Pythonie reprezentowane sa przez typ obiektowy, który stanowi łącze do pliku przechowywanego w komputerze i udostępnia szereg metod zdefiniowanych dla obiektów plikowych (pozwalających m. in. na zapis i czytanie z pliku). Pierwszym krokiem, wymaganym podczas korzystania z pliku, jest zatem utworzenie skojarzonego z nim obiektu plikowego służy do tego wbudowana funkcja open(), której najprostsze wywołanie dla istniejącego pliku testowego przedstawiono poniżej. Działanie tej funkcji polega na otwarciu pliku (domyślnie do odczytu) i zwróceniu tego pliku jako obiektu: [edytad@pk102 edytad]$ cat plik_testowy.txt to jest 1-sza linia i jakies liczby: 123 5.23 101.9 3.121 0.987 2 [edytad@pk102 edytad]$ python >>> plik = open( plik_testowy.txt ) >>> type(plik) <type file > Argumentem funkcji open() jest łańcuch, będący nazwą pliku, która zawierać może względną lub bezwzględną ścieżkę dostępu (przy braku scieżki, plik wyszukiwany jest w katalogu bieżącym). Opcjonalny drugi argument (pominiety powyżej) określa sposób otwarcia pliku: r tylko do odczytu (wartość domyślna; plik musi istnieć), w tylko do zapisu (istniejący plik o danej nazwie zostanie nadpisany), a tylko do dopisywania na końcu istniejącego pliku (jeśli plik nie istnieje, zostanie utworzony). Otwarcie pliku nie jest równoznaczne z odczytem jego zawartości. Do tego celu wykorzystywane sa trzy metody obiektów plikowych: read(), readline() oraz readlines(). Pierwsza wczytuje zawartość pliku jako pojedynczy łańcuch (opcjonalnie można podać liczbę bajtów do wczytania), druga odczytuje bieżącą linię z pliku i również zwraca łańcuch, wreszcie ostatnia z przedstawionych metod zwraca listę, zawierającą łańcuchy odpowiadające poszczególnym liniom z pliku. Python wewnętrznie śledzi bieżącą pozycję pliku, czyli miejsce, gdzie zatrzymała się ostatnia operacja zapisu bądź odczytu. Każdy kolejny odczyt zaczyna się w miejscu, w którym zakończył się poprzedni. Koniec odczytu następuje z chwilą napotkania końca pliku (EOF). Jeżeli operacja odczytu zaczyna się od EOF, zwracany jest pusty łańcuch (w przypadku pustej linii w pliku zwracany jest łańcuch 8
4 PODSTAWOWE OPERACJE NA PLIKACH n ). Zmiany bieżącej pozycji w pliku dokonać można za pomocą metody seek(), która przesuwa miejsce odczytu/zapisu o określoną ilość bajtów. Wywołanie seek(0) przestawia aktualną pozycję w pliku na jego początek. Aby zakończyć połączenie z plikiem zewnętrznym, należy wywołać metodę close(). Python automatycznie zamyka plik z chwilą zakończenia działania skryptu, ale zbyt wiele otwartych plików niepotrzebnie zajmuje zasoby. >>> plik.read(7) to jest >>> plik.readline() 1-sza linia\n >>> plik.readline() i jakies liczby:\n >>> plik.readlines() [ 123\t5.23\t101.9\n, 3.121\t0.987\t2\n ] >>> plik.seek(0) #powrot na poczatek pliku >>> plik.readline() to jest 1-sza linia >>> plik.close() Zapis do pliku umożliwiają metody write() i writelines() odpowiednio zapis pojedynczego łańcucha lub listy łańcuchów. Żadna z tych metod nie dopisuje na końcu zapisywanych łańcuchów znaku nowego wiersza. Plik musi być wcześniej otwarty do zapisu. Ponieważ zapisywane dane poczatkowo zapamiętywane są w buforze, aby mieć pewność, że pojawiły się one w pliku, należy albo go zamknać (close() powoduje automatyczny zapis zawartości bufora do pliku), lub wywołać metodę flush(), która wymusza zapis danych z bufora do pliku bez zamykania tego ostatniego. >>> plik2 = open( plik_do_zapisu.txt, w ) >>> plik2.write( Poczatek 1 linii ) >>> plik2.write( i koniec\n ) >>> plik2.writelines([ jeszcze druga\n, i trzecia linia\n ]) >>> plik2.close() >>> print open( plik_do_zapisu.txt ).read() Poczatek 1 linii i koniec jeszcze druga i trzecia linia Jak widać w linii zawierającej instrukcję print w powyższym kodzie, zmienna plikowa nie musi być utworzona, aby przeprowadzać operacje na pliku. Metoda open() zwraca pewną konkretną war- 9
4 PODSTAWOWE OPERACJE NA PLIKACH tość obiektu plikowego, na której można bezpośrednio wykonywać dostępne dla tego obiektu metody. W takim wypadku zamknięcie pliku następuje automatycznie zaraz po odczytaniu jego zawartości. Obsługa dużych plików oznacza często sekwencyjne przegladanie pliku i np. wyszukiwanie określonego ciągu znaków lub przetwarzanie jego fragmentów. Zapamiętywanie całej zawartości pliku jako pojedynczego łańcucha (lub listy łańcuchów) jest nieekonomiczne i ograniczone dostępną ilością pamięci operacyjnej. W takich wypadkach odczyt i zapis powinny odbywać się w pętli dopóty, dopóki Python nie napotka końca pliku lub nie zostanie spełniony inny warunek. Podany poniżej kod ilustruje sytuację, gdy odczyt pliku ma zostać zakończony po napotkaniu określonego łańcucha i wyświetleniu linii zawierającej ten łańcuch (na przykładzie utworzonego poprzednio pliku plik_do_zapisu.txt): >>> plik3 = open( plik_do_zapisu.txt ) >>> while 1: linia = plik3.readline() if not linia: #koniec pliku oznacza pusty lancuch! print Koniec pliku break if linia.find( druga )!= -1: print Znaleziono!, linia, break Znaleziono! jeszcze druga >>> plik3.close() Przykładem jednoczesnego odczytu i zapisu w pętli może być przepisanie pliku na nowy, połączone z wprowadzeniem zmian w jego zawartości: >>> plik4 = open( plik_do_zapisu.txt ) >>> plik5 = open( plik_ze_zmianami.txt, w ) >>> while 1: linia = plik4.readline() if not linia: break plik5.write(linia.title()) >>> plik4.close(); plik5.close() >>> print open( plik_ze_zmianami.txt ).read() Poczatek 1 Linii I Koniec # itd. 10