Podsekcje

 
4. Jeszcze więcej sposobów na kontrolowanie programu

Python wyposażony jest w zestaw wielu instrukcji, nie tylko w while, którą już poznaliśmy. Są one dobrze znane z innych języków programowania, choć posiadają swój lokalny koloryt.

 
4.1 Instrukcje if

Zdaje mi się, że najbardziej znaną instrukcją jest instrukcja if. Np.:

>>> x = int(raw_input("Proszę podać liczbę: "))
>>> if x < 0:
...      x = 0
...      print 'Ujemna zmieniła się na zero'
... elif x == 0:
...      print 'Zero'
... elif x == 1:
...      print 'Jeden'
... else:
...      print 'Więcej'
...

Można wstawić zero lub więcej części elif, a część else jest opcjonalna. Słowo kluczowe elif jest skrótem instrukcji 'else if' i przydaje się, gdy chce się uniknąć kolejnej indentacji. Sekwencja if ... elif ... elif ... zastępuje instrukcje switch lub case spotykane w innych językach.4.1

 
4.2 Instrukcje for

Instrukcja for różni się troszeczkę w Pythonie od tego, co używasz w C lub Pascalu. Nie prowadzi się iteracji od liczby do liczby (jak w Pascalu) lub daje się użytkownikowi możliwość definiowania kroku iteracji i warunki zakończenia iteracji (jak w C). Instrukcja for w Pythonie powoduje iterację po elementach jakiejkolwiek sekwencji (np. listy lub łańcucha znaków), w takim porządku, w jakim są one umieszczone w danej sekwencji. Na przykład:

>>> # Mierzy pewne napisy:
... a = ['kot', 'okno', 'wypróżnić']
>>> for x in a:
...     print x, len(x)
...
kot 3
okno 4
wypróżnić 9

Nie jest bezpiecznym modyfikacja sekwencji, która właśnie jest przedmiotem iteracji (można to zrobić tylko dla mutowalnego typu sekwencji, tzn. listy). Jeśli chce się ją modyfikować, np. duplikować wybrane jej elementy, to przeprowadź iterację na jej kopii. Notacja wykrawania jest tu szczególnie użyteczna:

>>> for x in a[:]: # wykrój całą listę (zrób jej kopię)
...    if len(x) > 6: a.insert(0, x)
...
>>> a
['wypróżnić', 'kot', 'okno', 'wypróżnić']

 
4.3 Funkcja range()

Jeśli zaszła potrzeba iteracji określonej zakresem liczbowym (czyli iteracji na sekwencji liczb w Pythonie), można użyć wbudowanej w interpreter funkcji range(). Wynikiem jej działania jest lista zawierająca ciąg arytmetyczny, tzn.:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Podany na jej wejściu punkt końcowy nigdy nie zostanie zawarty w wynikowej liście. range(10) tworzy listę 10 wartości, a tak naprawdę dokładnie zbiór dopuszczalnych wartości indeksów dla listy o długości 10. Jeśli jest taka potrzeba, można określić liczbę początkową tego ciągu albo krok (nawet liczbę ujemną):

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

Aby przeglądnąć wszystkie elementy listy łączy się funkcje range() i len(), tak jak poniżej:

>>> a = ['Marysia', 'miała', 'małego', 'baranka']
>>> for i in range(len(a)):
...     print i, a[i]
...
0 Marysia
1 miała
2 małego
3 baranka

 
4.4 Instrukcja break i continue oraz klauzule else w pętlach

Instrukcja break, podobnie jak w C, powoduje wyjście z najbliżej zagnieżdżonej pętli for lub while.

Instrukcja continue została również zapożyczona z C, powoduje przejście do następnego kroku iteracji w pętli.

Instrukcje pętli posiadają klauzulę else: jest ona wykonywana w momencie zakończenia działania pętli przy wyczerpaniu się (dojścia za ostatni element) listy (pętla for) lub gdy warunek pętli zwraca wartość fałszu (pętla while), ale nie w momencie opuszczenia pętli w wyniku zadziałania instrukcji break. Pokazane to zostało na przykładzie algorytmu poszukiwania liczby pierwszej:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...            print n, 'równe', x, '*', n/x
...            break
...     else:
...          print n, 'jest liczbą pierwszą'
...
2 jest liczbą pierwszą
3 jest liczbą pierwszą
4 równe 2 * 2
5 jest liczbą pierwszą
6 równe 2 * 3
7 jest liczbą pierwszą
8 równe 2 * 4
9 równe 3 * 3

 
4.5 Instrukcje pass

Instrukcja pass nic nie robi. Może być użyta wszędzie tam, gdzie wymagana jest jakaś instrukcja z powodów składniowych, ale program nie przewiduje w tym miejscu żadnego działania. Na przykład:

>>> while 1:
...       pass # Zajęty-poczekaj na naciśnięcie klawisza
...

 
4.6 Definiowanie funkcji

Możemy stworzyć funkcję, która wypisuje ciąg Fibonaciego o wybranych granicach:

>>> def fib(n):    # wypisz ciąg Fibonacciego aż do n
...     "Wypisuje ciąg Fibonacciego aż do n"
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
...
>>> # Teraz, wywołajmy funkcję, którą przed chwilą zdefiniowaliśmy:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Słowo kluczowe def wprowadza definicję funkcji. Musi po nim następować nazwa funkcji i lista jej parametrów formalnych umieszczonych w nawiasach okrągłych. Instrukcje, które tworzą ciało funkcji, są oczywiście wsunięte w stosunku do wiersza zawierającego nazwę funkcji i muszą zaczynać się w nowym wierszu. Opcjonalnie, pierwszy wiersz ciała funkcji może być gołym napisem (literałem): jest to tzw.  napis dokumentujący lub (inna nazwa tego zjawiska) docstring. 

Istnieją pewne narzędzia, które używają napisów dokumentacyjnych (docstringów) do automatycznego tworzenia drukowanej dokumentacji albo pozwalają użytkownikowi na interaktywne przeglądanie kodu. Dobrym zwyczajem jest pisane napisów dokumentacyjnych w czasie pisania programu: spróbuj się do tego przyzwyczaić.

Wykonanie funkcji powoduje stworzenie nowej tablicy symboli lokalnych używanych w tej funkcji. Mówiąc precyzyjniej: wszystkie przypisania do zmiennych lokalnych funkcji powodują umieszczenie tych wartości w lokalnej tablicy symboli, z czego wynika, że odniesienia do zmiennych najpierw szukają swych wartości w lokalnej tablicy symboli, a potem w globalnej, a dopiero na końcu w tablicy nazw wbudowanych w interpreter. Tak więc, zmiennym globalnym nie można wprost przypisać wartości w ciele funkcji (chyba, że zostaną wymienione w niej za pomocą instrukcji global), aczkolwiek mogą w niej być używane (czytane).

Parametry wywołania funkcyjnego (argumenty) wprowadzane są do lokalnej tablicy symboli w momencie wywołania funkcji. Tak więc, argumenty przekazywane są jej przez wartość (gdzie wartość jest zawsze odniesieniem do obiektu, a nie samym obiektem).4.2Nowa tablica symboli tworzona jest również w przypadku, gdy funkcja wywołuje inną funkcję.

Definicja funkcji wprowadza do aktualnej tablicy symboli nazwę tej funkcji. Nazwa ta identyfikuje wartość, której typ rozpoznawany jest przez interpreter jako funkcja zdefiniowana przez użytkownika. Wartość ta (a właściwie obiekt ) może być przypisana innej nazwie, która potem może zostać użyta jak funkcja. Ta właściwość może posłużyć jako ogólny mechanizm zmiany nazw:

>>> fib
<function object at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

Możnaby tutaj naprostować moje opowieści, że fib nie jest funkcją, ale procedurą. W Pythonie, podobnie jak w C, procedury są specyficznymi funkcjami, które nie zwracają wartości4.3 Tak naprawdę, mówiąc językiem technicznym, procedury naprawdę zwracają wartość, aczkolwiek raczej nudną.4.4 Wartość ta nazywana jest None (jest to nazwa wbudowana). Napisanie wartości None jest w normalnych warunkach pomijane przez interpreter, jeżeli jest to jedyna wartość, która miała być wypisana. Można ją zobaczyć, jeżeli naprawdę tego się chce:

>>> print fib(0)
None

Bardzo proste jest napisanie funkcji, która zwraca listę liczb ciągu Fibonacciego, zamiast wypisywać je:

>>> def fib2(n): # zwraca wartości ciągu Fibonacciego aż do n
...     "Zwraca wartości ciągu Fibonacciego, aż do n"
...     wynik = []
...     a, b = 0, 1
...     while b < n:
...         wynik.append(b)    # zobacz poniżej
...         a, b = b, a+b
...     return wynik
...
>>> f100 = fib2(100)    # wywołaj ją
>>> f100                # wypisz wynik
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Ten przykład,, jak zwykle, demonstruje parę nowych pythonowatych właściwości:

 
4.7 Jeszcze więcej o definiowaniu funkcji

Możliwe jest definiowanie funkcji ze zmienną liczbą argumentów. Istnieją trzy formy takiej definicji, które mogą być ze sobą splatane.

 
4.7.1 Domyślne wartości argumentów

Najbardziej użyteczną formą jest określenie domyślnej wartości dla jednego lub większej liczby argumentów. W ten sposób funkcja może zostać wywołana z mniejszą liczbą argumentów, niż była zdefiniowana, tzn.:

def zapytaj_ok(zacheta, liczba_prob=4, zazalenie='Tak lub nie, bardzo
proszę!'):       while 1:
        ok = raw_input(zacheta)
        if ok in ('t', 'ta', 'tak'): return 1
        if ok in ('n', 'nie', 'ee', 'gdzie tam'): return 0
        liczba_prob = liczba_prob - 1
        if liczba_prob < 0: raise IOError, 'użytkownik niekumaty'
        print zazalenie

Funkcja ta może zostać wywołana w taki sposób: zapytaj_ok('Naprawdę chcesz zakończyć?') lub w taki: zapytaj_ok('Zgadzasz się nadpisać ten plik?', 2).

Wartości domyślne określane są w punkcie definicji funkcji, w przestrzeni definiowania nazw, tak więc np.:

i = 5
def f(arg = i): print arg
i = 6
f()

wypisze 5.

Ważne ostrzeżenie: wartość domyślna określana jest tylko raz. Ma to znaczenie w przypadku, gdy wartością tą jest obiekt mutowalny, jak np. lista czy słownik. W przykładzie poniżej, funkcja akumuluje argumenty przekazane jej w kolejnych wywołaniach:

def f(a, l = []):
    l.append(a)
    return l
print f(1)
print f(2)
print f(3)

Instrukcje print spowodują:

[1]
[1, 2]
[1, 2, 3]

jeśli nie chce się, aby wartości domyślne były współdzielone pomiędzy kolejnymi wywołaniami funkcji, można ją napisać w taki sposób:

def f(a, l = None):
    if l is None:
        l = []
    l.append(a)
    return l

 
4.7.2 Argumenty kluczowe

Funkcja może być wywołana z użyciem argumentów kluczowych, tzn. w formie "klucz = wartość". Na przykład, poniższa funkcja:

def papuga(napiecie, stan='racja', akcja='voom', typ='Norwegian Blue'):
    print "-- Ta papuga nie zrobiłaby", akcja
    print "jeśli przyłożysz", napiecie, "woltów do niej."
    print "-- Śliczne upierzenie, ten", typ
    print "-- Tak,", stan, "!"

mogłaby być wywołana na parę różnych sposobów:

papuga(1000)
papuga(akcja = 'VOOOOOM', napiecie = 1000000)
papuga('tysiąc', stan = 'już wącha kwiatki od spodu')
parrot('milion', 'bereft of life', 'skoku')

lecz poniższe wywołania byłyby nieprawidłowe:

papuga()                     # brakuje wymaganego argumentu
papuga(napiecie=5.0, 'trup')  # niekluczowy argument za kluczowym
papuga(110, napiecie=220)     # zduplikowana wartość argumentu
papuga(aktor='John Cleese')  # nieznana nazwa argumentu

W ogólności mówiąc,4.5lista argumentów wywołania, musi mieć jakiś argument pozycyjny, po którym następuje jakikolwiek argument kluczowy, gdzie klucze wybrane są z listy parametrów formalnych. Nie jest ważne, czy parametr formalny ma wartość domyślną, czy też nie. Żaden z argumentów nie może otrzymać wartości więcej niż jeden raz -- nazwy parametrów formalnych odpowiadające argumentom pozycyjnym w wywołaniu nie mogą być w nim użyte jako kluczowe. Oto przykład wywołania, które się nie powiedzie z uwagi na wymienione przed chwilą ograniczenia:

>>> def function(a):
...     pass
...
>>> function(0, a=0)
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: keyword parameter redefined

Gdy na liście parametrów formalnych funkcji widnieje **nazwa, to przy wywołaniu funkcji przypisywany jest mu słownik zawierający wszystkie klucze, które nie odpowiadają nazwom parametrów formalnych. Mechanizm ten może być połączony z wystąpieniem parametru formalnego o nazwie *nazwa (co zostanie opisane w następnym podrozdziale), który w momencie wywołania staje się krotką (ang. tuple), która zawiera wszystkie argumenty pozycyjne (niekluczowe) wymienione w wywołaniu funkcji za parametrami formalnymi. (*nazwa musi pojawić się przed **nazwa na liście parametrów formalnych). Jeżeli, na przykład, zdefiniujemy funkcję w ten sposób:

def sklep_z_serami(rodzaj, *argumenty, **klucze):
    print "-- Czy macie", rodzaj, '?'
    print "-- Przykro mi,", rodzaj, "właśnie się skończył."
    for arg in argumenty: print arg
    print '-'*40
    for kl in klucze.keys(): print kl, ':', klucze[kl]

która mogłaby być wywołana o tak4.6:

sklep_z_serami('Limburger', "Jest bardzo dojrzały, proszę pana.",
           "Jest naprawdę bardzo, BARDZO dojrzały, proszę pana.",
           klient='John Cleese',
           wlasciciel='Michael Palin',
           skecz='Skecz ze sklepem z serami')

co oczywiście spowoduje wypisanie:

-- Czy macie Limburger ?
-- Przykro mi, Limburger właśnie się skończył.
Jest bardzo dojrzały, proszę pana.
Jest naprawdę bardzo, BARDZO dojrzały, proszę pana.
----------------------------------------
klient : John Cleese
wlasciciel : Michael Palin
skecz : Skecz ze sklepem z serami

 
4.7.3 Lista arbitralnych argumentów

Ostatnim sposobem na to, aby funkcja została wywołana w dowolną liczbą argumentów jest wprost określenie tego w definicji. Wszystkie argumenty zostaną zapakowane w krotkę.4.7 Przedtem na liście argumentów może pojawić się zero lub więcej normalnych argumentów.

def fprintf(plik, format, *args):
    plik.write(format % args)

 
4.7.4 Formy lambda

Ze względu na wzrastającą liczbę głosów użytkowników, dodano do Pythona parę nowych właściwości spotykanych zwykle w językach funkcjonalnych. Za pomocą słowa kluczowego lambda możesz tworzyć małe, anonimowe (czyli nienazwane) funkcje. Oto funkcja, która zwraca sumę jej dwóch argumentów: "lambda a, b: a+b". Formy lambda mogą zostać użyte we wszystkich miejscach, gdzie wymagane są obiekty funkcji. Składniowo ograniczone są do pojedynczego wyrażenia. Semantycznie, są właściwie szczyptą cukru na składnię zwykłych definicji funkcji. Podobnie jak zagnieżdżone definicje funkcji, formy lambda nie mogą używać nazw zmiennych z zakresu zewnętrznego,, lecz może to być ominięte poprzez sprytne użycie argumentów domyślnych:

def stworz_powiekszacza(n):
    return lambda x, incr=n: x+incr

 
4.7.5 Napisy dokumentujące

Istnieje pewna konwencja dotycząca zawartości i formatowania napisów dokumentujących.  

Pierwszy wiersz powinien być krótki, zwięźle podsumowujący działanie obiektu. Dla zwięzłości nie powinien wprost określać nazwy obiektu lub jego typu albowiem atrybuty te są dostępne dla czytelnika za pomocą innych środków (z wyjątkiem czasownika opisującego działanie funkcji). wiersz ten powien zaczynać się od dużej litery i kończyć kropką.

Jeśli napis zawiera więcej linii, drugi wiersz powinien być pusty, wizualnie oddzielając resztę opisu. Następne wiersze powinny obejmować jeden lub więcej akapitów, opisując sposób wywołania obiektu, efekty uboczne itd.

Parser Pythona nie wycina znaków tabulacji i wcięcia z wielowierszowych literałów napisów, dlatego też narzędzia do produkcji dokumentacji muszą to robić same, jeśli jest to wymagane. Robi się to wg następującej konwencji. Pierwszy niepusty wiersz po pierwszym wierszu napisu określa wielkość wsunięcia całego napisu dokumentującego. (Nie można użyć pierwszego wiersza, gdyż zazwyczaj położony jest bliżej otwierającego napis cydzysłowia, tak, że indentacja nie jest widoczna). Ekwiwalent tego wcięcia złożony z ,,białych znaków'' (czyli spacji, tabulacji) jest wycinany z początków wierszy całego napisu. Wiersze, które są wcięte mniej niż ta ilość, nie powinny się w napisie pojawić, ale jeśli to się stanie, to wszystkie znaki białe z przodu wiersza zostaną usunięte. Równowartość wcięcia liczona w spacjach powinna być sprawdzona po rozwinięciu znaków tabulacji (zazwyczaj do 8 spacji).

Oto przykład wielowierszowego docstring'u:

>>> def moja_funkcja():
...     """Nie rób nic, ale udokumentuj to.
...
...     Naprawdę, tutaj niczego się nie robi.
...     """
...     pass
...
>>> print moja_funkcja.__doc__
Nie rób nic, ale udokumentuj to.

     Naprawdę, tutaj niczego się nie robi.



... językach.4.1
Czyli instrukcje wyboru.
... obiektem).4.2
Właściwie wywołanie przez odniesienie, byłoby lepszym określeniem, ponieważ jeżeli przekazywane jest odniesienie do obiektu mutowalnego, to wywołujący funkcję zobaczy wszystkie zmiany dokonane na takim obiekcie (np. elementy wstawione do listy).
... wartości4.3
Więc nie są to funkcje. W C lub C++procedury-funkcje zwracają wartość void (przynajmniej składniowo)! I tam na pewno są to funkcje, specyficzne, ale zawsze funkcje!
... nudną.4.4
Czyli jednak są to funkcje!
... mówiąc,4.5
jak mawiał Nikuś Dyzma
... tak4.6
To jest bardzo nieudolne tłumaczenie tego prześmiesznego skeczu. Ś.p. Beksiński zrobił to wiele śmieszniej: niestety, nie miałem tego nagrania przy sobie
... krotkę.4.7
listę niemutowalną
Zajrzyj do Informacji na temat tej publikacji... aby pomóc w jej rozwoju.