# 4. Więcej narzędzi kontroli przepływu

Oprócz wprowadzonej przed chwilą instrukcji `while`, Python używa zwykłych instrukcji kontroli przepływu znanych z innych języków, z drobnymi zmianami.

## 4.1. Instrukcje `if`

Prawdopodobnie najbardziej znanym typem instrukcji jest instrukcja `if`. Na przykład:

    >>> x = int(input("Please enter an integer: "))
    Please enter an integer: 42
    >>> if x < 0:
    ...     x = 0
    ...     print('Negative changed to zero')
    ... elif x == 0:
    ...     print('Zero')
    ... elif x == 1:
    ...     print('Single')
    ... else:
    ...     print('More')
    ...
    More

Części `elif` może być zero lub więcej i część `else` jest opcjonalna. Keyword „`elif`” jest skrótem od „else if” i jest przydatny by uniknąć nadmiarowych wcięć. Sekwencja `if` … `elif` … `elif` … jest zamiennikiem instrukcji `switch` lub `case` z innych języków.

Jeśli porównujesz tę samą wartość z wieloma stałymi lub sprawdzasz poszczególne typy lub atrybuty, może ci się przydać instrukcja `match`. Więcej szczegółów znajdziesz w Instrukcje match.

## 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 (listy lub łańcucha znaków), w takim porządku, w jakim są one umieszczone w danej sekwencji. Na przykład (gra słów niezamierzona):

    >>> # Measure some strings:
    ... words = ['cat', 'window', 'defenestrate']
    >>> for w in words:
    ...     print(w, len(w))
    ...
    cat 3
    window 6
    defenestrate 12

Kod, który zmienia kolekcję podczas iterowania po niej, może być trudny. Zamiast tego, zazwyczaj prostsze jest przejść pętlą po kopii kolekcji lub stworzyć nową kolekcję:

    # Create a sample collection
    users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}

    # Strategy:  Iterate over a copy
    for user, status in users.copy().items():
        if status == 'inactive':
            del users[user]

    # Strategy:  Create a new collection
    active_users = {}
    for user, status in users.items():
        if status == 'active':
            active_users[user] = status

## 4.3. Funkcja `range()`

Jeśli potrzebujesz iterować po sekwencji liczb, przydatna jest wbudowana funkcja `range()`. Generuje ciągi arytmetyczne:

    >>> for i in range(5):
    ...     print(i)
    ...
    0
    1
    2
    3
    4

Podany punkt końcowy nigdy nie jest częścią generowanej sekwencji; `range(10)` generuje 10 wartości, poprawne indeksy dla elementów sekwencji o długości 10. Możliwe jest zacząć zakres od innej liczby lub podać inne zwiększenie (nawet ujemne; czasem jest to nazywane „krokiem”):

    >>> list(range(5, 10))
    [5, 6, 7, 8, 9]

    >>> list(range(0, 10, 3))
    [0, 3, 6, 9]

    >>> list(range(-10, -100, -30))
    [-10, -40, -70]

By przeiterować po indeksach sekwencji możesz połączyć `range()` i `len()` w następujący sposób:

    >>> a = ['Mary', 'had', 'a', 'little', 'lamb']
    >>> for i in range(len(a)):
    ...     print(i, a[i])
    ...
    0 Mary
    1 had
    2 a
    3 little
    4 lamb

Jednak w większości takich przypadków wygodnie jest użyć funkcji `enumerate()`, patrz Techniki pętli.

Dzieje się dziwna rzecz jeśli po prostu wydrukujesz zakres:

    >>> range(10)
    range(0, 10)

Pod wieloma względami obiekt zwracany przez `range()` zachowuje się, jakby był listą, ale w rzeczywistości nią nie jest. Jest obiektem, który zwraca kolejne elementy żądanej sekwencji w trakcie twojego iterowania po nim, lecz naprawdę nie tworzy listy, tak więc oszczędza miejsce w pamięci komputera.

Mówimy, że taki obiekt to iterable, to znaczy odpowiedni jako cel dla funkcji i konstrukcji, które spodziewają się czegoś, z czego można pobierać kolejne elementy aż do wyczerpania zapasu. Widzieliśmy, że instrukcja `for` jest takim konstruktem, podczas gdy przykładem funkcji, która spodziewa się obiektu iterable jest `sum()`:

    >>> sum(range(4))  # 0 + 1 + 2 + 3
    6

Później napotkamy więcej funkcji, które zwracają iterable i biorą iterable jako argumenty. W rozdziale Struktury danych, omówimy bardziej szczegółowo `list()`.

## 4.4. `break` i `continue` oraz klauzula `else` w pętlach

Instrukcja `break`, tak jak w C, wychodzi z najbardziej wewnętrznej pętli `for` lub `while` zawierającej tę instrukcję.

Instrukcje pętli mogą mieć klauzulę `else`. Jest ona wykonywana, gdy pętla kończy się przez wyczerpanie się iterable’a (w pętli `for`) lub kiedy warunek staje się fałszywy (w pętli `while`), lecz nie gdy pętli jest przerwana przez instrukcję `break`. Ilustruje to poniższa pętla, która wyszukuje liczby pierwsze:

    >>> for n in range(2, 10):
    ...     for x in range(2, n):
    ...         if n % x == 0:
    ...             print(n, 'equals', x, '*', n//x)
    ...             break
    ...     else:
    ...         # loop fell through without finding a factor
    ...         print(n, 'is a prime number')
    ...
    2 is a prime number
    3 is a prime number
    4 equals 2 * 2
    5 is a prime number
    6 equals 2 * 3
    7 is a prime number
    8 equals 2 * 4
    9 equals 3 * 3

(Tak, to poprawny kod. Przyjrzyj się: klauzula `else` należy do pętli `for`, nie do instrukcji `if`.)

Gdy używasz jej z pętlą, klauzula `else` ma więcej wspólnego z klauzulą `else` instrukcji `try` niż z tą z instrukcji `if`: klauzula `else` instrukcji `try` jest uruchamiana, gdy nie zostanie zgłoszony wyjątek, a klauzula `else` pętli jest uruchamiana, gdy nie wystąpi `break`. Więcej informacji na temat instrukcji `try` i wyjątków znajdziesz w Obsługa wyjątków.

Instrukcja `continue`, również pożyczona z C, kontynuuje następną iterację pętli:

    >>> for num in range(2, 10):
    ...     if num % 2 == 0:
    ...         print("Found an even number", num)
    ...         continue
    ...     print("Found an odd number", num)
    ...
    Found an even number 2
    Found an odd number 3
    Found an even number 4
    Found an odd number 5
    Found an even number 6
    Found an odd number 7
    Found an even number 8
    Found an odd number 9

## 4.5. Instrukcje `pass`

Instrukcja `pass` nie robi nic. Można jej użyć, gdy składnia wymaga instrukcji a program nie wymaga działania. Na przykład:

    >>> while True:
    ...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
    ...

Często jej się używa do tworzenia minimalnych klas:

    >>> class MyEmptyClass:
    ...     pass
    ...

Innym miejscem, w którym można użyć `pass` to placeholder dla funkcji lub ciała warunku, kiedy pracujesz nad nowym kodem. Pozwoli ci to myśleć na bardziej abstrakcyjnym poziomie. `pass` jest „po cichu” ignorowane:

    >>> def initlog(*args):
    ...     pass   # Remember to implement this!
    ...

## 4.6. Instrukcje `match`

Instrukcja `match` bierze wyrażenie i porównuje jego wartość do kolejnych wzorców podanych jako jeden lub więcej blok case. Jest to powierzchownie podobne do instrukcji switch w C, Javie lub JavaScripcie (i wielu innych językach), ale bardziej jest podobne do pattern matchingu w takich językach jak Rust lub Haskell. Jedynie pierwszy pasujący wzorzec jest zostaje wykonany i może on również wydobywać komponenty (elementy sekwencji lub atrybuty obiektu) z wartości do zmiennych.

Najprostsza forma porównuje wartość podmiotu z jednym lub wieloma literałami:

    def http_error(status):
        match status:
            case 400:
                return "Bad request"
            case 404:
                return "Not found"
            case 418:
                return "I'm a teapot"
            case _:
                return "Something's wrong with the internet"

Zwróć uwagę na ostatni blok: „nazwa zmiennej” `_` zachowuje się jak dzika karta i zawsze znajduje dopasowanie. Jeśli żaden z case’ów nie będzie dopasowany, żadne z rozgałęzień nie będzie wykonane.

Możesz łączyć kilka literałów w jeden wzorzec używając `|` („or”):

    case 401 | 403 | 404:
        return "Not allowed"

Wzorce mogą wyglądać jak przypisania rozpakowujące i można ich używać do powiązywania zmiennych:

    # point is an (x, y) tuple
    match point:
        case (0, 0):
            print("Origin")
        case (0, y):
            print(f"Y={y}")
        case (x, 0):
            print(f"X={x}")
        case (x, y):
            print(f"X={x}, Y={y}")
        case _:
            raise ValueError("Not a point")

Przyjrzyj się temu uważnie! Pierwszy wzorzec ma dwa literały i można o nim myśleć jako o rozszerzeniu dosłownego wzorca pokazanego wyżej. Ale następne dwa wzorce składają się z literału i zmiennej. Ta zmienna wiąże wartość z podmiotu (`point`). Czwarty wzorzec wyłapuje dwie wartości, co czyni go konceptualnie bliższym do przypisania z „rozpakowaniem” `(x, y) = point`.

Jeśli używasz klas, aby nadać strukturę swoim danym, możesz użyć nazwę klasy oraz listę argumentów przypominającą konstruktor, ale z możliwością wyłapania atrybutów w zmienne:

    class Point:
        x: int
        y: int

    def where_is(point):
        match point:
            case Point(x=0, y=0):
                print("Origin")
            case Point(x=0, y=y):
                print(f"Y={y}")
            case Point(x=x, y=0):
                print(f"X={x}")
            case Point():
                print("Somewhere else")
            case _:
                print("Not a point")

Możesz użyć parametrów pozycyjnych z jakimiś wbudowanymi klasami, które posiadają kolejność dla atrybutów (na przykład dataclasses). Możesz też określić wybrane pozycje dla atrybutów we wzorcach ustawiając specjalny atrybut `__match_args__` w twoich klasach. Jeśli jest on ustawiony na („x”, „y”), wszystkie następujące wzorce są równoważne (i wszystkie powiązują atrybut `y` ze zmienną `var`):

    Point(1, var)
    Point(1, y=var)
    Point(x=1, y=var)
    Point(y=var, x=1)

Rekomendowanym sposobem na czytanie wzorców jest patrzenie na nie jako na rozszerzoną formę tego, co wstawił(a)byś po lewej stronie przypisania, aby zrozumieć które zmienne będą ustawione na co. Tylko samodzielne nazwy (jak `var` powyżej) zyskują przypisanie w instrukcji match. Nazwy z kropkami (jak `foo.bar`), nazwy atrybutów (`x=` i `y=` powyżej) lub nazwy klas (rozpoznawane przez „(…)” przy nich jak `Point` powyżej) nie mają nigdy przypisań.

Wzorce mogą być dowolnie zagnieżdżane. Na przykład jeśli mamy krótką listę punktów, możemy je dopasowywać w ten sposób:

    match points:
        case []:
            print("No points")
        case [Point(0, 0)]:
            print("The origin")
        case [Point(x, y)]:
            print(f"Single point {x}, {y}")
        case [Point(0, y1), Point(0, y2)]:
            print(f"Two on the Y axis at {y1}, {y2}")
        case _:
            print("Something else")

Możemy dodać klauzulę `if` do wzorca, nazywaną „guardem”. Jeśli guard jest fałszywy, `match` próbuje dopasować następny blok case. Zwróć uwagę, że przechwycenie wartości dzieje się zanim wyliczona jest wartość guarda:

    match point:
        case Point(x, y) if x == y:
            print(f"Y=X at {x}")
        case Point(x, y):
            print(f"Not on the diagonal")

Kilka innych ważnych funkcjonalności tej instrukcji:

- Podobnie do przypisań z „rozpakowywaniem”, wzorce z krotkami i listami mają to samo znaczenie i dopasowują się do dowolnych sekwencji. Ważnym wyjątkiem jest to, że nie dopasowują się do iteratorów i ciągów znaków.

- Wzorce sekwencji wspierają rozszerzone rozpakowywanie: `[x, y, *rest]` i `(x, y, *rest)` działają podobnie do przypisań z rozpakowywaniem. Nazwa po `*` może być również `_`, aby `(x, y, *_)` dopasowywała się do sekwencji o conajmniej dwóch elementach bez powiązywania ze zmienną pozostałych elementów.

- Wzorce mapowania: `{"bandwidth": b, "latency": l}` przechwytuje wartości `"bandwidth"` i `"latency"` ze słownika. W przeciwieństwie do wzorców sekwencji, nadmiarowe klucze są ignorowane. Rozpakowywanie typu `**rest` jest również wspierane. (Ale `**_` byłoby nadmierne, więc jest niedozwolone.)

- Można przechwytywać podwzorce używając słowa kluczowego `as`:

      case (Point(x1, y1), Point(x2, y2) as p2): ...

  przechwyci drugi element wejścia jako `p2` (jeśli wejście jest sekwencją dwóch punktów)

- Większość literałów jest porównywanych przez równość, lecz singletony `True`, `False` i `None` są porównywane przez identyczność.

- Wzorce mogą używać nazwanych stałych. Trzeba je podawać po kropce, aby uniknąć interpretacji przechwycenia jako zwykłej zmiennej:

      from enum import Enum
      class Color(Enum):
          RED = 'red'
          GREEN = 'green'
          BLUE = 'blue'

      color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))

      match color:
          case Color.RED:
              print("I see red!")
          case Color.GREEN:
              print("Grass is green")
          case Color.BLUE:
              print("I'm feeling the blues :(")

Bardziej szczegółowe wyjaśnienie i dodatkowe przykłady możesz znaleźć w PEP 636, który jest napisany w formie tutoriala.

## 4.7. Definiowanie funkcji

Możemy stworzyć funkcję, która wypisuje ciąg Fibonacciego do wskazanej granicy:

    >>> def fib(n):    # write Fibonacci series up to n
    ...     """Print a Fibonacci series up to n."""
    ...     a, b = 0, 1
    ...     while a < n:
    ...         print(a, end=' ')
    ...         a, b = b, a+b
    ...     print()
    ...
    >>> # Now call the function we just defined:
    ... fib(2000)
    0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Słowo kluczowe `def` oznacza definicję funkcji. Po nim musi następować nazwa funkcji oraz lista formalnych parametrów otoczona nawiasami. Instrukcje, które stanowią ciało funkcji zaczynają się w następnej linii i muszą być wcięte.

Opcjonalnie, pierwszy wiersz ciała funkcji może być gołym napisem (literałem): jest to tzw. napis dokumentujący lub inaczej docstring. (Więcej o docstringach znajdziesz w sekcji Napisy dokumentujące (docstringi).) Istnieją pewne narzędzia, które używają docstringów do automatycznego tworzenia drukowanej lub dostępnej online 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. Odniesienia do zmiennych najpierw szukają swych wartości w lokalnej tablicy symboli, potem w lokalnych tablicach symboli funkcji otaczających, potem w globalnej, a dopiero na końcu w tablicy nazw wbudowanych w interpreter. Tak więc, zmiennym globalnym ani zmiennym w otaczających funkcjach nie można wprost przypisać wartości w ciele funkcji (chyba, że zostaną wymienione w niej za pomocą instrukcji `global` lub dla zmiennych w otaczających funkcjach, wymienionych w instrukcji `nonlocal`), aczkolwiek mogą w niej być używane (czytane).

Parametry (argumenty) wywołania funkcji 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). 1 Nowa tablica symboli tworzona jest również w przypadku, gdy funkcja wywołuje inną funkcję lub wywołuje się przez rekursję.

Definicja funkcji powiązuje nazwę funkcji z obiektem funkcji w aktualnej tablicy symboli. Interpreter rozpoznaje obiekt wskazany tą nazwą jako funkcję zdefiniowaną przez użytkownika. Inne nazwy też mogą wskazywać na ten sam obiekt funkcji i mogą być używane, aby dostać się do funkcji:

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

Przychodząc z innych języków, mógłbyś oponować, że `fib` nie jest funkcją, ale procedurą, jako że nie zwraca wartości. Tak naprawdę nawet funkcje bez instrukcji `return` zwracają wartość, chociaż dość nudną. Tę wartość nazywamy `None` (to wbudowana nazwa). Wypisywanie wartości `None` jest normalnie pomijane przez interpreter, jeśli miałaby to jedyna wypisywana wartość. Możesz ją zobaczyć, jeśli bardzo chcesz, używając `print()`:

    >>> fib(0)
    >>> print(fib(0))
    None

Prosto można napisać funkcję, która zwraca listę numerów ciągu Fibonnaciego zamiast go wyświetlać:

    >>> def fib2(n):  # return Fibonacci series up to n
    ...     """Return a list containing the Fibonacci series up to n."""
    ...     result = []
    ...     a, b = 0, 1
    ...     while a < n:
    ...         result.append(a)    # see below
    ...         a, b = b, a+b
    ...     return result
    ...
    >>> f100 = fib2(100)    # call it
    >>> f100                # write the result
    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Ten przykład, jak zazwyczaj, prezentuje nowe cechy Pythona:

- Instrukcja `return` zwraca wartość funkcji. `return` bez wyrażenia podanego jako argument zwraca `None`. Dojście do końca funkcji również zwraca `None`.

- Instrukcja `result.append(a)` wywołuje metodę listy obiektów `result`. Metoda to funkcja, która „należy” do obiektu i jest nazwana `obj.methodname`, gdzie `obj` jest jakimś obiektem (może też być wyrażeniem) a `methodname` jest nazwą metody, które jest zdefiniowana przez typ obiektu. Różne typy definiują różne metody. Metody różnych typów mogą mieć te same nazwy bez powodowania dwuznaczności. (Da się definiować własne typy obiektów i metody, używając klas, patrz Klasy.) Metoda `append()` pokazana w przykładzie jest zdefiniowana dla listy obiektów; dodaje nowy element na końcu listy. W tym przykładzie jest równoważna `result = result + [a]`, ale bardziej wydajna.

## 4.8. Więcej o definiowaniu funkcji

Można też definiować funkcje ze zmienną liczbą argumentów. Są trzy sposoby, które można łączyć.

### 4.8.1. Domyślne wartości argumentów

Najbardziej przydatnym sposobem jest podanie domyślnej wartości dla jednego lub więcej argumentów. Tworzy to funkcję, która może zostać wywołana z mniejszą liczbą argumentów, niż jest podane w jej definicji. Na przykład:

    def ask_ok(prompt, retries=4, reminder='Please try again!'):
        while True:
            ok = input(prompt)
            if ok in ('y', 'ye', 'yes'):
                return True
            if ok in ('n', 'no', 'nop', 'nope'):
                return False
            retries = retries - 1
            if retries < 0:
                raise ValueError('invalid user response')
            print(reminder)

Tę funkcję można wywołać na kilka sposobów:

- podając tylko wymagany argument: `ask_ok('Czy na pewno chcesz wyjść?')`

- podając jeden z opcjonalnych argumentów: `ask_ok('OK nadpisać plik?', 2)`

- lub podając wszystkie argumenty: `ask_ok('OK nadpisać plik?', 2, 'No coś ty, tylko tak lub nie!')`

Ten przykład wprowadza słowo kluczowe `in`. Sprawdza ono, czy sekwencja zawiera szczególną wartość.

Wartości domyślne są ewaluowane w momencie definiowania funkcji w scopie defining, więc

    i = 5

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

    i = 6
    f()

wyświetli `5`.

Ważna uwaga: Wartość domyślna jest wyliczana tylko raz. Ma to znaczenie, gdy domyślna wartość jest obiektem mutowalnym takim jak lista, słownik lub instancje większości klas. Na przykład następująca funkcja akumuluje argumenty przekazane do niej w kolejnych wywołaniach:

    def f(a, L=[]):
        L.append(a)
        return L

    print(f(1))
    print(f(2))
    print(f(3))

To wyświetli

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

Jeśli nie chcesz, żeby domyślna wartość była współdzielona pomiędzy kolejnymi wywołaniami, możesz napisać funkcję w ten sposób:

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

### 4.8.2. Argumenty nazwane

Funkcje mogą być również wywoływane przy użyciu argumentów nazwanych w formie `kwarg=value`. Na przykład poniższa funkcja:

    def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
        print("-- This parrot wouldn't", action, end=' ')
        print("if you put", voltage, "volts through it.")
        print("-- Lovely plumage, the", type)
        print("-- It's", state, "!")

akceptuje jeden wymagany argument (`voltage`) i trzy opcjonalne argumenty (`state`, `action` i `type`). Funkcja może być wywołana w dowolny z poniższych sposobów:

    parrot(1000)                                          # 1 positional argument
    parrot(voltage=1000)                                  # 1 keyword argument
    parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
    parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
    parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
    parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

ale wszystkie poniższe wywołania byłyby niepoprawne:

    parrot()                     # required argument missing
    parrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argument
    parrot(110, voltage=220)     # duplicate value for the same argument
    parrot(actor='John Cleese')  # unknown keyword argument

W wywołaniu funkcji argumenty nazwane muszą znajdować się za argumentami pozycyjnymi. Wszystkie przekazane argumenty nazwane muszą pasować do jednego argumentu akceptowanego przez funkcję (na przykład `actor` nie jest poprawnym argumentem dla funkcji `parrot`) a ich kolejność nie ma znaczenia. Dotyczy to również nie-opcjonalnych argumentów (na przykład `parrot(voltage=1000)` też jest poprawne). Żaden argument nie może otrzymać wartości więcej niż raz. Tutaj jest przykład, który się nie powiedzie z powodu tego ograniczenia:

    >>> def function(a):
    ...     pass
    ...
    >>> function(0, a=0)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: function() got multiple values for argument 'a'

Kiedy ostatni formalny parametr ma postać `**nazwa`, to otrzymuje on słownik (zobacz Mapping Types — dict) zawierający wszystkie argumenty kluczowe oprócz tych które odpowiadają formalnym parametrom. Może to być połączone z formalnym parametrem o postaci `*nazwa` (opisanym w następnej subsekcji) który otrzymuje krotkę zawierającą argumenty pozycyjne z poza listy formalnych parametrów. (`*nazwa` musi występować przed `**nazwa`.) Dla przykładu możemy zdefiniować funkcję tak:

    def cheeseshop(kind, *arguments, **keywords):
        print("-- Do you have any", kind, "?")
        print("-- I'm sorry, we're all out of", kind)
        for arg in arguments:
            print(arg)
        print("-" * 40)
        for kw in keywords:
            print(kw, ":", keywords[kw])

Można ją wywołać w ten sposób:

    cheeseshop("Limburger", "It's very runny, sir.",
               "It's really very, VERY runny, sir.",
               shopkeeper="Michael Palin",
               client="John Cleese",
               sketch="Cheese Shop Sketch")

i oczywiście wyświetli się nam:

    -- Do you have any Limburger ?
    -- I'm sorry, we're all out of Limburger
    It's very runny, sir.
    It's really very, VERY runny, sir.
    ----------------------------------------
    shopkeeper : Michael Palin
    client : John Cleese
    sketch : Cheese Shop Sketch

Zwróć uwagę, że kolejność w jakim argumenty kluczowe są wyświetlane dokładnie odpowiada kolejności w jakim zostały one podane w wywołaniu funkcji.

### 4.8.3. Parametry specjalne

Domyślnie, argumenty mogą być przekazywane do funkcji Pythona albo poprzez swoje umiejscowienie (pozycję) albo wprost poprzez słowo kluczowe. Dla czytelności oraz wydajności, ma sens aby ograniczyć sposób w jaki argumenty mogą być przekazywane tak aby deweloper musiał tylko spojrzeć na definicję funkcji aby zrozumieć czy argumenty są przekazywane poprzez pozycję, pozycję i słowa kluczowe czy same słowa kluczowe.

Definicja funkcji może wyglądać w ten sposób:

    def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
          -----------    ----------     ----------
            |             |                  |
            |        Positional or keyword   |
            |                                - Keyword only
             -- Positional only

gdzie `/` i `*` są opcjonalne. Jeśli są używane, te symbole wskazują rodzaj parametru poprzez sposób przekazywania argumentów do funkcji: tylko pozycyjne, pozycyjne lub słowo kluczowe i tylko słowo kluczowe. Parametry słów kluczowych są również nazywane parametrami nazwanymi.

#### 4.8.3.1. Argumenty pozycyjne-lub-nazwane

Jeśli `/` czy `*` nie są obecne w definicji funkcji, argumenty mogą być przekazywane i poprzez pozycje i przez słowa kluczowe.

#### 4.8.3.2. Parametry tylko-pozycyjne (positional-only)

Wchodząc w szegóły, można oznaczyć pewne parametry jako tylko-pozycyjne. Jeśli argument jest tylko pozycyjny, ma znaczenie kolejność parametrów a parametry te nie mogą być przekazywane przez słowa kluczowe. Argumenty tylko-pozycyjne są umieszczane przed `/` (forward-slash). `/` rozdziela parametry tylko-pozycyjne od reszty parametrów. Jeśli w definicji funkcji nie ma `/` , nie ma parametrów tylko pozycyjnych.

Argumenty po `/` mogą mieć charakter pozycja-lub-słowo-kluczowe lub tylko-kluczowe.

#### 4.8.3.3. Argumenty tylko-nazwane (keyword-only)

Aby oznaczyć parametry jako tylko słowa kluczowe (keyword-only), to znaczy wskazać, że parametry muszą być przekazywane przez argument słowa kluczowego, umieść `*` na liście argumentów tuż przed pierwszym parametrem, który ma być przekazywany tylko przez słowo kluczowe.

#### 4.8.3.4. Przykłady funkcji

Rozważ następujące przykłady definicji funkcji, przykładając szczególną uwagę do oznaczeń `/` oraz `*`:

    >>> def standard_arg(arg):
    ...     print(arg)
    ...
    >>> def pos_only_arg(arg, /):
    ...     print(arg)
    ...
    >>> def kwd_only_arg(*, arg):
    ...     print(arg)
    ...
    >>> def combined_example(pos_only, /, standard, *, kwd_only):
    ...     print(pos_only, standard, kwd_only)

Pierwsza z definicji, `standard_arg`, mająca najbardziej znaną formę, nie wprowadza ograniczeń na sposób wywoływania, argumenty mogą być przekazywane poprzez pozycję lub słowo kluczowe:

    >>> standard_arg(2)
    2

    >>> standard_arg(arg=2)
    2

Druga z funkcji `pos_only_arg` jest ograniczona do wykorzystywania tylko argumentów pozycyjnych jako, że posiada `/` w swojej definicji:

    >>> pos_only_arg(1)
    1

    >>> pos_only_arg(arg=1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'

Trzecia z funkcji `kwd_only_args` pozwala tylko na wprowadzanie argumentów w postaci słów kluczowych z powodu `*` w swojej definicji:

    >>> kwd_only_arg(3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

    >>> kwd_only_arg(arg=3)
    3

Ostatnia z funkcji wykorzystuje wszystkie trzy konwencje w swojej definicji:

    >>> combined_example(1, 2, 3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: combined_example() takes 2 positional arguments but 3 were given

    >>> combined_example(1, 2, kwd_only=3)
    1 2 3

    >>> combined_example(1, standard=2, kwd_only=3)
    1 2 3

    >>> combined_example(pos_only=1, standard=2, kwd_only=3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'

Na koniec, przyjrzyj się definicji funkcji, która ma potencjalną kolizję pomiędzy pozycyjnym argumentem `name` a `**kwds` gdzie `name` jest kluczem:

    def foo(name, **kwds):
        return 'name' in kwds

Nie jesteśmy w stanie sprawić, aby ta funkcja zwróciła `True`, jako że słowo kluczowe `'name'` zawsze powiązane będzie z pierwszym parametrem. Dla przykładu:

    >>> foo(1, **{'name': 2})
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: foo() got multiple values for argument 'name'
    >>>

Korzystając z `/` (argumentów tylko-pozycyjnych), da się to zrobić ponieważ pozwala ono na `name` jako argument pozycyjny i `'name'` jako klucz w argumentach kluczowych:

    def foo(name, /, **kwds):
        return 'name' in kwds
    >>> foo(1, **{'name': 2})
    True

Innymi słowy, nazwy w argumentach pozycyjnych mogą być bez dwuznaczności wykorzystywane jako klucze w `**kwds`.

#### 4.8.3.5. Podsumowanie

Przypadki użycia determinują, jakich typów parametrów kiedy w definicji funkcji:

    def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

Kieruj się następującym:

- Wykorzystuj argumenty tylko-pozycyjne gdy chcesz aby nazwa parametrów nie była dostepna dla użytkownika. To może być pomocne gdy nazwy parametów nie mają specjalnego znaczenia a ty chcesz wymusić określoną kolejność argumentów gdy funkcja jest wywoływana. Ewentualnie gdy chcesz aby część argumentów była pozycyjna a część była jakimiś słowami kluczowymi.

- Wykorzystuj argumenty tylko-kluczowe gdy nazwy mają znaczenie i definicja funkcji jest łatwiejsza do zrozumienia poprzez wprost podawanie nazw, lub jeśli nie chcesz aby użytkownicy polegali na kolejności argumentów.

- W przypadku budowania API, wykorzystanie argumentów tylko-pozycyjnych pozwoli w przyszłości uniknąć problemów, gdy nazwa parametru jest zmieniona.

### 4.8.4. Arbitralne listy argumentów

Najmniej często wykorzystywaną opcją jest specyfikowanie, że funkcja może być wywoływana z arbitralną liczbą argumentów. Takie argumenty zostaną opakowane w krotkę (zobacz krotki). Przed zmienną liczbą argumentów, można wymusić jeden lub więcej argumentów.

    def write_multiple_items(file, separator, *args):
        file.write(separator.join(args))

Zazwyczaj takie argumenty będą ostatnie na liście formalnych parametrów, ponieważ zbierają one wszystkie pozostałe argumenty przekazane funkcji. Każdy formalny argument po parametrze `*args` może być «tylko-kluczowy», to znaczy da się go wprowadzić tylko poprzez słowo kluczowe a nie przez pozycję.

    >>> def concat(*args, sep="/"):
    ...     return sep.join(args)
    ...
    >>> concat("earth", "mars", "venus")
    'earth/mars/venus'
    >>> concat("earth", "mars", "venus", sep=".")
    'earth.mars.venus'

### 4.8.5. Rozpakowywanie listy argumentów

Odwrotna sytuacja wystąpi, gdy argumenty już są listą albo krotką a muszą być rozpakowane gdyż funkcja wymaga argumentów przekazanych pozycyjnie, jeden po drugim . Dla przykładu, wbudowana funkcja `range()` oczekuje oddzielnych argumentów start oraz stop. Jeśli nie są dostępne oddzielnie, wywołaj funkcją z operatorem `*` aby wypakować argumenty z listy lub krotki:

    >>> list(range(3, 6))            # normal call with separate arguments
    [3, 4, 5]
    >>> args = [3, 6]
    >>> list(range(*args))            # call with arguments unpacked from a list
    [3, 4, 5]

W podobny sposób, słowniki moga dostarczać argumentów kluczowych poprzez operator `**`:

    >>> def parrot(voltage, state='a stiff', action='voom'):
    ...     print("-- This parrot wouldn't", action, end=' ')
    ...     print("if you put", voltage, "volts through it.", end=' ')
    ...     print("E's", state, "!")
    ...
    >>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
    >>> parrot(**d)
    -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

### 4.8.6. Wyrażenia Lambda

Niewielkie anonimowe funkcje mogą byś tworzone z wykorzystaniem słowa kluczowego `lambda`. Wykorzystując tą notację: `lambda a, b: a+b` powstanie funkcja sumująca podane argumenty. Funkcje lambda mogą być wykorzystywane zawsze wtedy gdy potrzebne są objekty funkcji. Są synaktycznie ograniczone do jednego wyrażenia. Semantycznie, jest to tylko lukier składniowy normalnej definicji funkcji. Podobnie jak funkcje zagnieżdżone funkcje lambda mogą odwoływać się do zmiennych z otaczającego zakresu

    >>> def make_incrementor(n):
    ...     return lambda x: x + n
    ...
    >>> f = make_incrementor(42)
    >>> f(0)
    42
    >>> f(1)
    43

Powyższy przykład wykorzystuje ekspresję lambda aby zwrócić funkcję. Inne wykorzystanie to przekazanie małej funkcji jako argumentu:

    >>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
    >>> pairs.sort(key=lambda pair: pair[1])
    >>> pairs
    [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

### 4.8.7. Napisy dokumentujące (docstringi)

Oto kilka konwencji dotyczących zawartości i formatowania docstringów.

Pierwsza linijka powinna zawierać zwięzłe streszczenie sensu jaki stoi za obiektem. Nie powinna wprost zawierać nazwy obiektu ani typu, gdyż te są dostępne w inny sposób (chyba, że nazwa jest czasownikiem opisujęcym działanie funkcji). Ta linijka powinna zaczynać się z dużej litery i kończyć kropką.

Jeśli w docstringu jest więcej niż jedna linijka, druga linijka powinna być pusta, aby rozdzielić ją od reszty opisu. Następne linijki powinny zawierać konwencje nazewnicze, efekty uboczne itd.

Parser Pythona nie usuwa wcięć z literału stringu wielolinijkowego, więc jeśli to jest pożądane, to narzędzia obrabiające dokumentację powinny usuwać wcięcia. Robi się to, wykorzystując następujące. Pierwsza nie-pusta linijka po pierwszej określa jak dużo wcięcia jest w całym docstringu. (Nie można do tego wykorzystać pierwszej linijki, ponieważ ta zazwyczaj przylega do cudzysłowów, więc sposób wcięcia jest nieoczywisty.) „Ilość wcięcia” z tej linijki jest następnie usuwana z tej i wszystkich następnych linijek. W kolejnych linijkach nie powinno być mniej wcięć ale jeśli tak będzie to całość wcięcia powinna być usunięta. Ilość wcięcia powinna być usuwana po zamianie tabulatorów na spacje (zazwyczaj na 8 spacji).

Poniżej przykład wielolinijkowego docstringu:

    >>> def my_function():
    ...     """Do nothing, but document it.
    ...
    ...     No, really, it doesn't do anything.
    ...     """
    ...     pass
    ...
    >>> print(my_function.__doc__)
    Do nothing, but document it.

        No, really, it doesn't do anything.

### 4.8.8. Adnotacje funkcji

Adnotacje funkcji to całkowicie opcjonalne metadane dające informacje o funkcjach zdefiniowanych przez użytkowników (zobacz PEP 3107 oraz PEP 484 aby uzyskać więcej informacji).

Adnotacje przechowywane są w atrybucie `__annotations__` funkcji jako słownik i nie mają oprócz żadnego innego wpływu na nią. Adnotacje parametrów są definiowane po nazwie parametru i dwukropku, jako wyrażenie sprawdzające wartość argumentu. Adonotacje do zwracanego wyniku definiuje się pomiędzy nawiasem z listą parametrów a drukropkiem kończącym instrukcję `def` , poprzez literał `->`, z następującym po nim wyrażeniem. Poniższy przykład ma adnotację dotyczącą argumentu wymaganego, argumentu opcjonalnego oraz adnotację zwracanego wyniku:

    >>> def f(ham: str, eggs: str = 'eggs') -> str:
    ...     print("Annotations:", f.__annotations__)
    ...     print("Arguments:", ham, eggs)
    ...     return ham + ' and ' + eggs
    ...
    >>> f('spam')
    Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
    Arguments: spam eggs
    'spam and eggs'

## 4.9. Intermezzo: Styl kodowania

Teraz, kiedy już jesteś gotowa(-wy) aby pisać dłuższe, bardziej złożone pythonowe dzieła, dobrze żebyśmy porozmawiali o stylu kodowania. Większość języków może być pisana (a mówiąc precyzyjniej, formatowana) w różnym stylu; bardziej lub mniej czytelnym. Zawsze dobrze jest dążyć, aby twój kod był łatwy do czytania przez innych a w tym bardzo pomaga stosowanie fajnego stylu kodowania.

Dla Pythona PEP 8 stał się wzorcem stylu, którego trzyma się większość projektów; szerzy czytelny i miły dla oka styl kodowania. W którymś momencie, powinien go przeczytać każdy developer Pythona, poniżej przedstawiliśmy jego najistotniejsze elementy:

- Jako wcięcie, wykorzystuj cztery spacje, nie tabulator.

  Cztery spacje są dobrym kompromisem pomiędzy płytkim wcięciem (pozwala na więcej kroków zagnieżdżania ) a głębokim wcięciem (jest łatwiejsze do przeczytania). Tabulatorów najlepiej nie używać, wprowadzają zamiesznie.

- Zawijaj linie tak, aby nie ich długość nie przekraczała 79 znaków.

  To pomoże użytkownikom z małymi wyświetlaczami a na większych ekranach pozwoli mieć kilka plików obok siebie na ekranie.

- Wstawiaj puste linie aby oddzielić od siebie funkcje, klasy lub większe bloki kodu wewnątrz funkcji.

- Jeśli jest to możliwe, wstawiaj komentarze w oddzielnej linii.

- Wykorzystuj docstringi

- Korzystaj ze spacji naokoło operatorów oraz za przecinkami, ale nie przy nawiasach: `a = f(1, 2) + g(3, 4)`.

- Nazywaj klasy i funkcje w konsekwentny sposób. Preferowaną konwencją jest „UpperCamelCase” dla nazw klas i „lowercase_with_underscores” dla funkcji i metod. Zawsze wykorzystuj „self” jako nazwę dla pierwszego argumentu metody (zobacz Pierwsze spojrzenie na klasy aby dowiedzieć się więcej o klasach i metodach).

- Nie wykorzystuj wymyślnych zestawów znaków jeśli twój kod będzie wykorzystywany międzynarodowo. Najlepiej trzymać się domyślnych w Pythonie: UTF-8 lub nawet ASCII.

- Podobnie, nie korzystaj ze znaków innych niż ASCII jako identyfikatorów, jeśli jest chociaż szansa, że osoby mówiące innym językiem będą czytać lub rozwijać Twój kod.

Przypisy

1

  Tak właściwie, wywołanie przez referencję do obiektu byłoby lepszym opisem, jako że jeśli mutowalny obiekt jest przekazany, wszystkie zmiany które wywoływany robi (elementy wstawiane na listę) będą widziane przez wywołującego.