JEZYK PROGRAMOWANIA PYTHON: WYRAŻENIA LOGICZNE I INSTRUKCJA WARUNKOWA, METODY OBIEKTÓW ŁAŃCUCHOWYCH E. Dyguda-Kazimierowicz 1 Wyrażenia i operatory logiczne Wyrażenia logiczne (warunkowe, boolowskie) to wyrażenia przyjmujące wartości: prawda lub fałsz. W Pythonie każdy obiekt, w zależnoṡci od jego zawartości, posiada wartość logiczną: dowolne puste struktury danych (np. łańcuch o zerowej długości) traktowane są jako fałszywe i odwrotnie (napis zawierający pojedynczą spację ma logiczną wartość prawda). W przypadku typu liczbowego wartości zerowe to fałsz, a niezerowe prawda. Obiekt pusty None ma logiczną wartość fałsz (obiekt None oznacza po prostu brak wartości). Operatory porównania (relacyjne) służą do porównywania dwóch obiektów. W zależności od wyniku, zwracana jest wartość False (fałsz) lub (prawda). Dostępne są następujące operatory: <, <=, >, >=, ==,!= (dwa ostatnie to odpowiednio: równość i nierówność). Sposób ich działania zależy od typu porównywanych obiektów: liczby porównywane są arytmetycznie (jeśli są różnego typu liczbowego, przed porównaniem następuje konwersja do wspólnego typu), łańcuchy na podstawie odpowiedników liczbowych poszczególnych znaków (porównywanie zatrzymuje się na pierwszych różnych znakach), obiekty różnych typów zawsze traktowane są jako nierówne: >>> x, y = 0.2, -3 >>> print x == y, x > y, x <= y, y!= x False False >>> z = 100 >>> print y < x <= z #porownanie kaskadowe >>> x == x False >>> ord( x ) #funkcja zwracajaca wartosc ASCII danego znaku 120 1
1 WYRAŻENIA I OPERATORY LOGICZNE >>> x < x >>> ord( X ) 88 >>> print ( X < x ), ( Xxx < xxx ), ( xxx < Xxx ) False Operatory logiczne działają na wartościach (lub całych wyrażeniach) logicznych. Jednoargumentowy operator not zmienia na przeciwną wartość logiczną swojego operandu (np. zwraca False, jeśli jego operand jest prawdziwy). Dwuargumentowe operatory and i or pozwalają określić czy wszystkie operandy są prawdziwe (w przypadku and), bądź czy conajmniej jeden z nich jest prawdziwy (w przypadku or). W przeciwieństwie do not, operatory and i or zwracają bezpośrednio jeden ze swoich operandów: and zwraca pierwszy fałszywy (lub prawdziwy, jeśli nie znajdzie fałszywego); operator or odwrotnie pierwszy prawdziwy (lub fałszywy, jeśli nie znajdzie prawdziwego). >>> print not, not -0.1 False >>> print 2 and 0, 2 and 3, 2 or 0, 2 or 3 0 3 2 2 >>> print or 0 or 2 or 3 2 >>> print a and 2 and 3 and 0 0 >>> ( xyz > xyz ) or ( nic!= NIC ) Podczas łączenia szeregu operatorów logicznych w złożonych wyrażeniach, ich priorytet maleje w kolejności not, and, or: >>> 2 and not nic or 123 # czyli ((2 and (not nic )) or 123) 123 Operator in pozwala zbadać przynależność danego obiektu do sekwencji obiektów, np. występowanie określonego znaku w łańcuchu: >>> print n in nic, N in nic False 2
2 INSTRUKCJA WARUNKOWA Operator in ma niższy priorytet w kolejności obliczeń od operatorów matematycznych, jeszcze niżej w tej hierarchii usytuowane są operatory porownań i dopiero po nich operatory logiczne. Tym samym w jednym z powyższych przykładów można opuścić nawiasy bez zmiany znaczenia wyrażenia: >>> xyz > xyz or nic!= NIC 2 Instrukcja warunkowa Instrukcje w kodzie źrodłowym Pythona wykonywane są jedna po drugiej według kolejności ich występowania, chyba że interpreter napotka jedną z instrukcji sterujacych umożliwiających zmianę przebiegu sterowania (np. pomijanie niektórych instrukcji, powtarzanie innych). Do tej grupy należy instrukcja warunkowa if, która uruchamia następujący po niej fragment kodu w zależności od wartości logicznej wchodzącego w jej skład warunku: >>> if 1: print "Wykonane!"... Wykonane! Ponieważ instrukcja if zawiera inne instrukcje (także zagnieżdżone if), określa się ją jako instrukcję złożona. Instrukcje złożone zaczynają się od wiersza nagłówkowego i następującego po nim ciągu instrukcji wspólnie tworzących blok: >>> if warunek: #wiersz naglowkowy zakonczony dwukropkiem... instrukcja1 #poczatek bloku instrukcji... instrukcja2 #kontynuacja bloku instrukcji... instrukcja3 #oraz ewentualne dalsze instrukcje... #koniec bloku instrukcji - w przypadku pracy interaktywnej Blok instrukcji rozpoznawany jest przez interpreter po wcięciu (dowolnej ilości spacji i znaków tabulacji, takiej samej w obrębie tego samego bloku) w stosunku do wiersza nagłówkowego. Podczas pracy interaktywnej, sygnałem zakończenia zagnieżdżonego bloku instrukcji jest wprowadzenie pustej linii; w programach wystarczy zmniejszyć wcięcie do szerokości takiej, jaką miał wiersz początkowy. Jeżeli po if ma się znaleźć pojedyncza instrukcja, można umieścić ją w tym samym wierszu (jak w pierwszym przykładzie). Przy okazji kilka uwag o składni Pythona zasady są proste i jednocześnie wymuszają zapis kodu w czytelnej postaci: 3
2 INSTRUKCJA WARUNKOWA jedna instrukcja jeden wiersz: koniec instrukcji rozpoznawany jest po przejściu do nowego wiersza instrukcje wcięte na taka sama głębokość blok instrukcji: nie trzeba stosować dodatkowych ograniczników (np. nawiasów) kilka instrukcji w jednym wierszu: oddzielenie średnikiem ; >>> x = -11; y = 0; z = 11 podział długiej instrukcji na wiersze: ograniczenie nawiasami wyrażenia, które ma być podzielone (sposób ukryty) lub wstawienie lewego ukośnika "\" na końcu linii, której kontynuacja ma nastąpić w kolejnym wierszu (sposób jawny; po znaku "\" nie można dopisać komentarza) >>> test1 = (... x % 2 == 0 and #podzielne przez 2... x % 4!= 0 or #i niepodzielne przez 4... x < y ) #ale za to mniejsze od y >>> print test1 >>> test2 = \... x <= y and \... y < z or \... x > z >>> print test2 W przedstawionej powyżej postaci, instrukcja if sprawdza prawdziwość warunku i jeśli jest on spełniony (ma wartość logiczną prawda), wykonuje blok instrukcji. Niespełniony warunek spowoduje pominięcie danego bloku. Uzupełnienie instrukcji if o klauzulę else (wciętą na taką samą głębokość jak wiersz nagłówkowy if) uruchomi następujący po niej drugi blok instrukcji właśnie w takim wypadku (tzn. gdy test związany z if zwróci fałsz): >>> if x % 2 == 0:... print "Liczba %d jest podzielna przez 2" % x... else:... print "Liczba %d nie jest podzielna przez 2" % x... Liczba -11 nie jest podzielna przez 2 4
3 METODY OBIEKTÓW ŁAŃCUCHOWYCH Aby sprawdzić kilka niezależnych warunków (instrukcja wielokrotnego wyboru), stosuje się klauzulę elif (skrót od else if). Blok instrukcji związany z końcowym else (else jest tu opcjonalne) zostanie wykonany, jeśli wszystkie wcześniejsze warunki zwrócą fałsz. >>> if x % 2 == 0:... print "Liczba %d jest podzielna przez 2" % x... elif x % 3 == 0:... print "Liczba %d jest podzielna przez 3" % x... elif x % 11 == 0:... print "Liczba %d jest podzielna przez 11" % x... else:... print "Liczba %d nie jest podzielna przez 2, 3, 11" % x... Liczba -11 jest podzielna przez 11 3 Metody obiektów łańcuchowych Dla programowania obiektowego charakterystyczne jest bezpośrednie związanie danych z przetwarzającymi je metodami dane i metody wspólnie tworzą jeden obiekt (np. łańcuchowy). W skład obiektu wchodzi również jego dokumentacja, stąd informacje o możliwych działaniach na obiekcie można znaleźć w nim samym (patrz: zastosowanie funkcji dir() do wyświetlania atrybutów obiektów w części 2 materiałów do zajęć). Metody należy rozumieć jako zwykłe funkcje działające na obiektach różnica polega na tym, że metody wchodzą w skład obiektu, na który działają. Każdorazowe utworzenie obiektu, czyli egzemplarza jakiegoś typu danych, jest równoznaczne z dziedziczeniem przez niego atrybutów zdefiniowanych dla całego typu, w tym jego metod. Różnicę między funkcjami i metodami można przedstawić na przykładzie obiektów łańcuchowych. W wersji 2.0 Pythona wprowadzono wbudowane metody obiektów łańcuchowych, pokrywające się w większości z używanymi do tej pory funkcjami działającymi na łańcuchy, zebranymi w osobnym module string. Zatem w tej chwili większość zadań związanych z łańcuchami można wykonać na dwa sposoby: z użyciem metody łańcuchowej lub funkcji z modułu string (oczywiście po jego uprzednim imporcie) proponuję porównać listę dostępnych metod i funkcji (odpowiednio dir( ) i dir(string)). Poniżej przykład sposobu wyświetlania łańcucha dokumentacji dla funkcji i równoważnej jej metody: 5
3 METODY OBIEKTÓW ŁAŃCUCHOWYCH >>> import string >>> print string.center. doc #dokumentacja funkcji center(s, width) -> string #i dalsza czesc opisu >>> print "".center. doc #dokumentacja metody S.center(width) -> string #tu reszta tez wycieta Metody i funkcje wywołuje się w różny sposób. Metoda jest w obiekcie, zatem kwalifikacja dotyczy po prostu nazwy obiektu, natomiast w przypadku funkcji, obiekt podawany jest jako argument (w zależności od sposobu importu modulu string, trzeba jeszcze uwzględnić kwalifikację jego nazwy): >>> napis = python >>> print [ + napis.center(10) + ] #metoda center [ python ] >>> print [ + string.center(napis, 10) + ] #funkcja center [ python ] Ponieważ w przyszłości moduł string zostanie prawdopodobnie wycofany, zaleca się korzystanie z wbudowanych metod łańcuchowych. Poniżej zestawiono kilka przydatnych metod obsługujących obiekty napisowe: zmiana wielkości liter: upper(), lower(), swapcase() odpowiednio przekształcenie wszystkich liter na wielkie, na małe bądź zamiana: małych na wielkie i odwrotnie capitalize() i title() pierwsza litera łańcucha lub pierwsze litery słów w łańcuchu przekształcane na wielkie, pozostałe na małe >>> To jest zdanie.upper() TO JEST ZDANIE >>> To jest zdanie.lower() to jest zdanie >>> To jest zdanie.swapcase() to JESt ZdaNIE >>> To jest zdanie.capitalize() To jest zdanie >>> To jest zdanie.title() To Jest Zdanie 6
3 METODY OBIEKTÓW ŁAŃCUCHOWYCH przycinanie łańcuchów: lstrip(), rstrip(), strip() usuwanie zbędnych odstępów na początku, na końcu lub po obu stronach łańcucha >>> \t jakis tam tekst \n.lstrip() jakis tam tekst \n >>> \t jakis tam tekst \n.rstrip() \t jakis tam tekst >>> \t jakis tam tekst \n.strip() jakis tam tekst wyrównywanie łańcuchów: ljust(int), rjust(int), center(int) wstawianie dodatkowych spacji aż do szerokości pola podanej w postaci argumentu int i wyrównanie tekstu w tym polu do lewej, prawej lub wyśrodkowanie >>> [ + wyrownywanie.ljust(20) + ] [wyrownywanie ] >>> [ + wyrownywanie.rjust(20) + ] [ wyrownywanie] >>> [ + wyrownywanie.center(20) + ] [ wyrownywanie ] testowanie łańcuchów: isalpha(), isdigit(), isalnum(), isspace() sprawdzają czy wszystkie znaki w łańcuchu są odpowiednio literami, cyframi, literami albo cyframi (znaki alfanumeryczne), znakami odstępu (spacja, znak tabulacji poziomej/pionowej, znak nowego wiersza/nowej strony, znak powrotu karetki); zwracają wartości logiczne lub False (prawda lub fałsz) isupper(), islower(), istitle() testują wielkość liter w łańcuchu (znaki nie będące literami są ignorowane) 7
3 METODY OBIEKTÓW ŁAŃCUCHOWYCH >>> xyz.isalpha(), xyz.isupper(), xyz.istitle() (, False, False) >>> 0x97f.isdigit(), 0x97f.isalnum(), 0x97f.islower() (False,, ) >>> To jest zdanie.istitle(), To Jest Zdanie. istitle() (False, ) >>> \n \f \t \v \r.isspace() Przynależność znaków do poszczególnych kategorii zdefiniowana jest jako szereg atrybutów modułu string (digits, hexdigits, octdigits, letters, lowercase, uppercase, punctuation, printable, whitespace), np.: >>> string.punctuation!"#$%&\ ()*+,-./:;<=>?@[\\]^_ { }~ wyszukiwanie i zamiana fragmentów łańcuchów: find(str), index(str), count(str), startswith(str), endswith(str), replace(str_stary, str_nowy) proponuję samodzielnie sprawdzić ich działanie Metody zwracające nowy napis można stosować sekwencyjnie, tzn. działać nimi po kolei na ten sam łańcuch. Wynikiem działania każdej z nich jest zmodyfikowany obiekt łańcuchowy, który nadal ma takie same metody, jak napis wyjściowy: >>> tekst= \n tu TRzeba troche poprawek...\t\f >>> tekst.strip() tu TRzeba troche poprawek... >>> tekst.strip().capitalize() Tu trzeba troche poprawek... >>> tekst.strip().capitalize().center(len(tekst) + 10) Tu trzeba troche poprawek... 8