# 5. Struktury danych

Ten rozdział opisuje bardziej szczegółowo niektóre rzeczy, które już poznaliście, a także dodaje kilka nowych.

## 5.1. Więcej na temat list

Typ danych list ma kilka dodatkowych metod. Poniżej znajdują się wszystkie metody obiektów typu listy:

list.append(value, /)

  Dodaje element na końcu listy. Podobne do `a[len(a):] = [x]`.

list.extend(iterable, /)

  Rozszerza listę przez dodanie wszystkich elementów iterable’a. Podobne do `a[len(a):] = iterable`.

list.insert(index, value, /)

  Wstawia element na podaną pozycję. Pierwszy argument jest indeksem elementu, przed który wstawiamy, więc `a.insert(0, x)` wstawia na początek listy a `a.insert(len(a), x)` odpowiada `a.append(x)`.

list.remove(value, /)

  Usuwa pierwszy element z listy, którego wartość jest równa value. Rzuca `ValueError`, jeśli nie ma takiego elementu.

list.pop(index=-1, /)

  Usuwa element na podanej pozycji na liście i zwraca go. Jeśli nie podano indeksu, `a.pop()` usuwa i zwraca ostatnią pozycję na liście. Funkcja rzuca `IndexError`, jeśli lista jest pusta lub indeks znajduje się poza zakresem listy.

list.clear()

  Usuwa wszystkie elementy z listy. Podobne do `del a[:]`.

list.index(value[, start[, stop]])

  Zwraca indeks (liczony od zera) pierwszego wystąpienia value w liście. Rzuca `ValueError`, jeśli nie ma takiego elementu.

  Opcjonalne argumenty start i end są interpretowane jak w notacji slice i służą do ograniczenia wyszukiwania do szczególnej podsekwencji listy. Zwracany indeks jest wyliczany względem początku pełnej sekwencji, nie względem argumentu start.

list.count(value, /)

  Zwraca liczbę razy, jaką value występuje w liście.

list.sort(*, key=None, reverse=False)

  Sortuje elementy listy w miejscu (argumenty mogą służyć do dostosowania sortowania, patrz `sorted()` po ich wyjaśnienie).

list.reverse()

  Odwraca elementy listy w miejscu.

list.copy()

  Zwraca płytką kopię listy. Podobne do `a[:]`.

Przykład, który używa większość metod listy:

    >>> fruits = ['pomarańcza', 'jabłko', 'winogrono', 'banan', 'kiwi', 'jabłko', 'banan']
    >>> fruits.count('jabłko')
    2
    >>> fruits.count('mandarynka')
    0
    >>> fruits.index('banan')
    3
    >>> fruits.index('banan', 4)  # znajdź następnego banana zaczynając od 4. pozycji
    6
    >>> fruits.reverse()
    >>> fruits
    ['banan', 'jabłko', 'kiwi', 'banan', 'winogrono', 'jabłko', 'pomarańcza']
    >>> fruits.append('gruszka')
    >>> fruits
    ['banan', 'jabłko', 'kiwi', 'banan', 'winogrono', 'jabłko', 'pomarańcza', 'gruszka']
    >>> fruits.sort()
    >>> fruits
    ['banan', 'banan', 'gruszka', 'jabłko', 'jabłko', 'kiwi', 'pomarańcza', 'winogrono']
    >>> fruits.pop()
    'winogrono'

Być może zauważyłeś(-łaś), że metody takie jak `insert`, `remove` czy `sort`, które tylko modyfikują listę, nie mają wypisanej wartości zwrotnej – zwracają domyślne `None`. [1] Jest to zasada projektowa dla wszystkich mutowalnych struktur danych w Pythonie.

Możesz też zauważyć, że nie wszystkie dane da się posortować lub porównać. Na przykład, `[None, 'hello', 10]` nie sortuje się, ponieważ liczby całkowite nie mogą być porównywane do ciągów znaków a `None` nie może być porównywany do innych typów. Są również typy, które nie mają określonej relacji porządku. Na przykład `3+4j < 5+7j` nie jest poprawnym porównaniem.

### 5.1.1. Używanie list jako stosów

Metody listy ułatwiają używanie listy jako stosu, gdzie ostatni element dodany jest pierwszym elementem pobieranym („last-in, first-out”). Aby dodać element na wierzch stosu, użyj `append()`. Aby pobrać element z wierzchu stosu, użyj `pop()` bez podanego indeksu. Na przykład:

    >>> stack = [3, 4, 5]
    >>> stack.append(6)
    >>> stack.append(7)
    >>> stack
    [3, 4, 5, 6, 7]
    >>> stack.pop()
    7
    >>> stack
    [3, 4, 5, 6]
    >>> stack.pop()
    6
    >>> stack.pop()
    5
    >>> stack
    [3, 4]

### 5.1.2. Używanie list jako kolejek

Można też używać list jako kolejek, gdzie pierwszy element dodany jest pierwszym elementem pobieranym („first-in, first-out”); jednakże listy nie są wydajne do tego celu. Appendy i popy na końcu listy są szybkie, lecz inserty i popy na początku listy są wolne (ponieważ wszystkie inne elementy muszą zostać przesunięte o jeden).

Aby zaimplementować kolejkę, użyj `collections.deque`, która została zaprojektowana, by mieć szybkie appendy i popy na obu końcach. Na przykład:

    >>> from collections import deque
    >>> queue = deque(["Eric", "John", "Michael"])
    >>> queue.append("Terry")           # pojawia się Terry
    >>> queue.append("Graham")          # pojawia się Graham
    >>> queue.popleft()                 # pierwszy, który się pojawił, teraz wychodzi
    'Eric'
    >>> queue.popleft()                 # drugi, który się pojawił, teraz wychodzi
    'John'
    >>> queue                           # pozostała kolejka w kolejności przybycia
    deque(['Michael', 'Terry', 'Graham'])

### 5.1.3. Wyrażenia listowe

Wyrażenia listowe są zwięzłym sposobem na tworzenie list. Powszechne zastosowania to tworzenie nowych list, gdzie każdy element jest wynikiem jakichś operacji zastosowanych do każdego elementu innej sekwencji lub iterable’a lub do tworzenia podsekwencji tych elementów, które spełniają określony warunek.

Na przykład załóżmy, że chcemy stworzyć listę kwadratów, jak tu:

    >>> squares = []
    >>> for x in range(10):
    ...     squares.append(x**2)
    ...
    >>> squares
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Zwróć uwagę, że ten kod tworzy (lub nadpisuje) zmienną o nazwie `x`, która wciąż istnieje po wykonaniu pętli. Możemy obliczyć listę kwadratów bez żadnych efektów ubocznych w następujący sposób:

    squares = list(map(lambda x: x**2, range(10)))

lub równoważnie:

    squares = [x**2 for x in range(10)]

co jest bardziej zwięzłe i czytelne.

Wyrażenie listowe składa się z nawiasów zawierających wyrażenie oraz klauzulę `for`, następnie zero lub więcej klauzul `for` lub `if`. Rezultatem będzie nowa lista powstała z obliczenia wyrażenia w kontekście klauzul `for` i `if`, które po nim następują. Na przykład to wyrażenie listowe łączy elementy dwóch list, jeśli nie są równe:

    >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
    [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

i jest odpowiednikiem:

    >>> combs = []
    >>> for x in [1,2,3]:
    ...     for y in [3,1,4]:
    ...         if x != y:
    ...             combs.append((x, y))
    ...
    >>> combs
    [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Zwróć uwagę, że kolejność instrukcji `for` i `if` jest taka sama w obu fragmentach kodu.

Jeśli wyrażenie jest krotką (tak jak `(x, y)` w poprzednim przykładzie), musi być wzięte w nawias.

    >>> vec = [-4, -2, 0, 2, 4]
    >>> # stwórz nową listę ze zdwojonymi wartościami
    >>> [x*2 for x in vec]
    [-8, -4, 0, 4, 8]
    >>> # przefiltruj listę, by wykluczyć liczby ujemne
    >>> [x for x in vec if x >= 0]
    [0, 2, 4]
    >>> # zastosuj funkcję do każdego elementu
    >>> [abs(x) for x in vec]
    [4, 2, 0, 2, 4]
    >>> # wywołaj metodę na każdym elemencie
    >>> freshfruit = ['  banan', '  malina ', 'marakuja  ']
    >>> [weapon.strip() for weapon in freshfruit]
    ['banan', 'malina', 'marakuja']
    >>> # stwórz listę dwukrotek jak (liczba, kwadrat)
    >>> [(x, x**2) for x in range(6)]
    [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
    >>> # krotka musi być w nawiasach, inaczej dostajemy błąd
    >>> [x, x**2 for x in range(6)]
      File "<stdin>", line 1
        [x, x**2 for x in range(6)]
         ^^^^^^^
    SyntaxError: did you forget parentheses around the comprehension target?
    >>> # spłaszcz listę używając list comprehension z dwoma 'for-ami'
    >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
    >>> [num for elem in vec for num in elem]
    [1, 2, 3, 4, 5, 6, 7, 8, 9]

Wyrażenia listowe mogą zawierać złożone wyrażenia i zagnieżdżone funkcje:

    >>> from math import pi
    >>> [str(round(pi, i)) for i in range(1, 6)]
    ['3.1', '3.14', '3.142', '3.1416', '3.14159']

### 5.1.4. Zagnieżdżone wyrażenia listowe

Wyrażeniem wyjściowym w wyrażeniu listowym może być każde arbitralne wyrażenie, włączając inne wyrażenie listowe.

Rozważmy następujący przykład macierzy 3-na-4 zaimplementowanej jako lista trzech list o długości 4:

    >>> matrix = [
    ...     [1, 2, 3, 4],
    ...     [5, 6, 7, 8],
    ...     [9, 10, 11, 12],
    ... ]

Następujące wyrażenie listowe przetransponuje wiersze i kolumny:

    >>> [[row[i] for row in matrix] for i in range(4)]
    [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Jak widzieliśmy w poprzedniej sekcji, wewnętrzne wyrażenie listowe jest ewaluowane w kontekście `for`, które po nim następuje, więc ten przykład jest równoważny temu:

    >>> transposed = []
    >>> for i in range(4):
    ...     transposed.append([row[i] for row in matrix])
    ...
    >>> transposed
    [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

który z kolei jest taki sam jak:

    >>> transposed = []
    >>> for i in range(4):
    ...     # następne 3 linie implementują zagnieżdżone list comprehension
    ...     transposed_row = []
    ...     for row in matrix:
    ...         transposed_row.append(row[i])
    ...     transposed.append(transposed_row)
    ...
    >>> transposed
    [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

W prawdziwym świecie powinieneś(-nnaś) preferować wbudowane funkcje w złożonych instrukcjach przepływu. Funkcja `zip()` bardzo się przyda w tym przypadku:

    >>> list(zip(*matrix))
    [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

W Rozpakowywanie listy argumentów znajdziesz wyjaśnienie znaku gwiazdki w tej linii.

## 5.2. Instrukcja `del`

Element można usunąć z listy mając jego indeks zamiast wartości: instrukcją `del`. Jest ona różna od metody `pop()`, która zwraca wartość. Instrukcji `del` można też użyć do usunięcia slice’ów lub wyczyszczenia całej listy (zrobiliśmy to wcześniej przypisując pustą listę do slice’a). Na przykład:

    >>> a = [-1, 1, 66.25, 333, 333, 1234.5]
    >>> del a[0]
    >>> a
    [1, 66.25, 333, 333, 1234.5]
    >>> del a[2:4]
    >>> a
    [1, 66.25, 1234.5]
    >>> del a[:]
    >>> a
    []

`del` można również użyć do usuwania całych zmiennych:

    >>> del a

Odniesienie się do nazwy `a` odtąd jest błędem (przynajmniej dopóki nie przypisana jest do niej inna wartość). Później odnajdziemy więcej zastosowań dla `del`.

## 5.3. Krotki i sekwencje

Widzieliśmy, że listy i ciągi znaków mają wiele wspólnych własności, takich jak indeksowanie i operacje slice. Są one dwoma przykładami sekwencyjnych typów danych (patrz Sequence Types — list, tuple, range). Jako że Python jest ewoluującym językiem, mogą zostać dodane inne sekwencyjne typy danych. Jest też inny standardowy sekwencyjny typ danych: krotka.

Krotka składa się z kilku wartości rozdzielonych przecinkami, na przykład:

    >>> t = 12345, 54321, 'dzień dobry!'
    >>> t[0]
    12345
    >>> t
    (12345, 54321, 'dzień dobry!')
    >>> # krotki mogą być zagnieżdżane:
    >>> u = t, (1, 2, 3, 4, 5)
    >>> u
    ((12345, 54321, 'dzień dobry!'), (1, 2, 3, 4, 5))
    >>> # krotki są niemutowalne
    >>> t[0] = 88888
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    >>> # ale mogą zawierać mutowalne obiekty:
    >>> v = ([1, 2, 3], [3, 2, 1])
    >>> v
    ([1, 2, 3], [3, 2, 1])

Jak widzisz na wyjściu krotki zawsze są otoczone nawiasami, tak aby zagnieżdżone krotki były poprawnie interpretowane; wpisać je można z lub bez otaczających nawiasów, chociaż często nawiasy są i tak potrzebne (jeśli krotka jest częścią większego wyrażenia). Nie da się przypisać wartości do pojedynczych elementów krotki, ale da się stworzyć krotki, które zawierają mutowalne obiekty, takie jak listy.

Mimo że krotki mogą wydawać się podobne do list, często są używane w innych sytuacjach i do innych celów. Krotki są niemutowalne i zazwyczaj zawierają heterogeniczne sekwencje elementów, do których dostęp uzyskuje się przez rozpakowywanie (patrz później w tej sekcji) lub indeksowanie (lub nawet przez atrybut w przypadku `namedtuples`). Listy są mutowalne i ich elementy są zazwyczaj homogeniczne i dostęp do nich uzyskuje się przez iterowanie po liście.

Specjalnym problemem jest konstrukcja krotek zawierających 0 lub 1 element: składnia przewiduje na to kilka sposobów. Puste krotki można konstruować pustą parą nawiasów; krotkę z jednym elementem można skonstruować umieszczając przecinek za wartością (nie wystarczy otoczyć pojedynczej wartości nawiasami). Brzydkie, ale działa. Na przykład:

    >>> empty = ()
    >>> singleton = 'cześć', # <— zwróć uwagę na przecinek na końcu
    >>> len(empty)
    0
    >>> len(singleton)
    1
    >>> singleton
    ('cześć',)

Instrukcja `t = 12345, 54321, 'hello!'` jest przykładem pakowania krotki: wartości `12345`, `54321` i `'hello!'` są razem zapakowane w krotkę. Możliwa jest również odwrotna operacja:

    >>> x, y, z = t

Takie coś nazywane jest, odpowiednio, rozpakowywaniem sekwencji. Takie rozpakowywanie wymaga, aby po lewej stronie znaku równości było tyle samo zmiennych, ile jest elementów w sekwencji. Zauważcie, że wielokrotne przypisanie jest kombinacją pakowania i rozpakowywania sekwencji.

## 5.4. Zbiory

Python ma również typ danych dla zbiorów. Zbiór jest nieuporządkowaną kolekcją bez zduplikowanych elementów. Podstawowe użycia to sprawdzenie zawierania i eliminacja duplikatów. Obiekty zbiorów wspierają też operacje matematyczne jak suma, iloczyn, różnica i różnica symetryczna zbiorów.

Zbiory można stworzyć używając nawiasów klamrowych lub funkcji `set()`. Uwaga: aby stworzyć pusty zbiór, musisz użyć `set()`, nie `{}`; to drugie tworzy pusty słownik, strukturę danych, którą omówimy w następnej sekcji.

Ponieważ zbiory są nieuporządkowane, iterowanie po nich lub ich drukowanie może spowodować wyświetlenie elementów w kolejności innej niż oczekiwana.

Poniżej krótka demonstracja:

    >>> basket = {'jabłko', 'pomarańcza', 'jabłko', 'gruszka', 'pomarańcza', 'banan'}
    >>> print(basket) # pokaż, że duplikaty zostały usunięte
    {'pomarańcza', 'banan', 'gruszka', 'jabłko'}
    >>> 'pomarańcza' in basket # szybkie sprawdzenie zawierania
    True
    >>> 'wiechlina' in basket
    False

    >>> # demonstracja operacji na zbiorach dla unikalnych liter z dwóch słów
    >>>
    >>> a = set('abrakadabra')
    >>> b = set('alakazam')
    >>> a # unikalne litery w a
    {'a', 'r', 'b', 'k', 'd'}
    >>> a - b # litery w a ale nie w b
    {'r', 'd', 'b'}
    >>> a | b # litery w a lub b lub w obu
    {'a', 'k', 'r', 'd', 'b', 'm', 'z', 'l'}
    >>> a & b # litery w obu a i b
    {'a', 'k'}
    >>> a ^ b # litery w a lub b ale nie w obu
    {'r', 'd', 'b', 'm', 'z', 'l'}

Podobnie do wyrażeń listowych, są wspierane również wyrażenia zbiorów:

    >>> a = {x for x in 'abrakadabra' if x not in 'abc'}
    >>> a
    {'r', 'k', 'd'}

## 5.5. Słowniki

Innym przydatnym typem danych wbudowanym w Pythona jest słownik (patrz Mapping Types — dict). Słowniki w innych językach czasem występują jako „pamięci asocjacyjne” albo „tablice asocjacyjne”. W przeciwieństwie do sekwencji, które są indeksowane zakresem liczb, słowniki są indeksowane przez klucze, które mogą być dowolnym niemutowalnym typem; ciągi znaków i liczby zawsze mogą być kluczami. Można użyć krotek, jeśli zawierają tylko ciągi znaków, liczby lub krotki; jeśli krotka zawiera choć jeden mutowalny obiekt, bezpośrednio lub pośrednio, nie można jej użyć jako klucza. Nie możesz używać list jako kluczy, jako że listy mogą być modyfikowane „w miejscu” przy użyciu przypisań do indeksu, przypisań do slice’ów lub metod jak `append()` i `extend()`.

Najlepiej jest myśleć o słowniku jako zbiorze par klucz: wartość, z wymaganiem aby klucze były unikalne (w obrębie jednego słownika). Para nawiasów klamrowych tworzy pusty słownik: `{}`. Umieszczenie listy par klucz:wartość rozdzielonych przecinkami dodaje początkowe pary do słownika; w ten sposób również słowniki są wypisywane na wyjściu.

Głównymi operacjami na słowniku są umieszczanie wartości pod jakimś kluczem oraz wyciąganie wartości dla podanego klucza. Możliwe jest również usunięcie pary klucz:wartość przy użyciu `del`. Jeśli umieścisz wartość używając klucza, który już jest w użyciu, stara wartość powiązana z tym kluczem zostanie zapomniana.

Pobieranie wartości dla nieistniejącego klucza przez operator index (`d[key]`) rzuca `KeyError`. Aby uniknąć tego błędu podczas próby pobierania potencjalnie nieistniejącego klucza, należy zamiast tego użyć metody `get()`, która zwraca `None` (lub określoną domyślną wartość), jeśli klucz nie znajduje się w słowniku.

Wykonanie `list(d)` na słowniku zwraca listę wszystkich kluczy używanych w słowniku, w kolejności wstawiania (jeśli chcesz je posortować, użyj `sorted(d)`). Aby sprawdzić, czy pojedynczy klucz jest w słowniku, użyj słowa kluczowego `in`.

Mały przykład użycia słownika:

    >>> tel = {'jack': 4098, 'sape': 4139}
    >>> tel['guido'] = 4127
    >>> tel
    {'jack': 4098, 'sape': 4139, 'guido': 4127}
    >>> tel['jack']
    4098
    >>> tel['irv']
    Traceback (most recent call last):
      File "", line 1, in
    KeyError: 'irv'
    >>> print(tel.get('irv'))
    None
    >>> del tel['sape']
    >>> tel['irv'] = 4127
    >>> tel
    {'jack': 4098, 'guido': 4127, 'irv': 4127}
    >>> list(tel)
    ['jack', 'guido', 'irv']
    >>> sorted(tel)
    ['guido', 'irv', 'jack']
    >>> 'guido' in tel
    True
    >>> 'jack' not in tel
    False

Konstruktor `dict()` buduje słowniki bezpośrednio z sekwencji par klucz-wartość:

    >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
    {'sape': 4139, 'guido': 4127, 'jack': 4098}

Dodatkowo można użyć wyrażeń słownikowych to tworzenia słowników z podanych wyrażeń klucza i wartości:

    >>> {x: x**2 for x in (2, 4, 6)}
    {2: 4, 4: 16, 6: 36}

Kiedy klucze są prostymi ciągami znaków, czasem łatwiej jest podać pary używając argumentów nazwanych:

    >>> dict(sape=4139, guido=4127, jack=4098)
    {'sape': 4139, 'guido': 4127, 'jack': 4098}

## 5.6. Techniki pętli

Podczas iterowania po słownikach, klucz i odpowiadającą mu wartość można pobrać w tym samym czasie używając metody `items()`.

    >>> knights = {'galahad': 'cnotliwy', 'robin': 'odważny'}
    >>> for k, v in knights.items():
    ...     print(k, v)
    ...
    galahad cnotliwy
    robin odważny

Przy iterowaniu po sekwencji, indeks pozycyjny i odpowiadającą mu wartość można pobrać w tym samym czasie używając funkcji `enumerate()`.

    >>> for i, v in enumerate(['kółko', 'i', 'krzyżyk']):
    ...     print(i, v)
    ...
    0 kółko
    1 i
    2 krzyżyk

Aby przeiterować po dwóch lub więcej sekwencjach w tym samym czasie, elementy mogą zostać zgrupowane funkcją `zip()`.

    >>> questions = ['imię', 'misja', 'ulubiony kolor']
    >>> answers = ['lancelot', 'święty graal', 'niebieski']
    >>> for q, a in zip(questions, answers):
    ...     print('Jego {0} to {1}.'.format(q, a))
    ...
    Jego imię to lancelot.
    Jego misja to święty graal.
    Jego ulubiony kolor to niebieski.

Aby przeiterować po sekwencji od końca, najpierw określ sekwencję w kierunku „do przodu” a następnie wywołaj funkcję `reversed()`.

    >>> for i in reversed(range(1, 10, 2)):
    ...     print(i)
    ...
    9
    7
    5
    3
    1

Aby przeiterować po sekwencji w posortowanej kolejności, użyj funkcji `sorted()`, która zwraca nową posortowaną listę pozostawiając listę źródłową niezmienioną.

    >>> basket = ['banan', 'jabłko', 'banan', 'pomarańcza', 'jabłko', 'gruszka']
    >>> for i in sorted(basket):
    ...     print(i)
    ...
    banan
    banan
    gruszka
    jabłko
    jabłko
    pomarańcza

Użycie `set()` na sekwencji eliminuje zduplikowane elementy. Użycie `sorted()` w połączeniu z `set()` na sekwencji jest idiomatycznym sposobem na przeiterowanie po unikalnych elementach sekwencji w posortowanej kolejności.

    >>> basket = ['banan', 'jabłko', 'banan', 'pomarańcza', 'jabłko', 'gruszka']
    >>> for f in sorted(set(basket)):
    ... print(f)
    ...
    banan
    gruszka
    jabłko
    pomarańcza

Czasem kusi, żeby zmienić listę podczas iterowania po niej; jednak często prościej i bezpieczniej jest stworzyć nową listę.

    >>> import math
    >>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
    >>> filtered_data = []
    >>> for value in raw_data:
    ...     if not math.isnan(value):
    ...         filtered_data.append(value)
    ...
    >>> filtered_data
    [56.2, 51.7, 55.3, 52.5, 47.8]

## 5.7. Więcej na temat warunków

Warunki użyte w instrukcjach `while` i `if` mogą zawierać dowolne operatory, nie tylko porównania.

Operatory porównania `in` i `not in` są testami należenia, które ustalają, czy wartość występuje (nie występuje) w kontenerze. Operatory `is` i `is not` porównują, czy dwa obiekty są rzeczywiście tym samym obiektem. Wszystkie operatory porównań mają ten sam priorytet, który jest niższy niż ten, który mają wszystkie operatory numeryczne.

Porównania mogą być układane w łańcuchy. Na przykład `a < b == c` sprawdza, czy `a` jest mniejsze od `b` i ponadto czy `b` równa się `c`.

Porównania można łączyć używając operatorów boolowskich `and` i `or`. Wynik porównania (lub jakiegokolwiek innego wyrażenia boolowskiego) można zanegować używając `not`. Operatory te mają mniejszy priorytet niż operatory porównania; wśród nich `not` ma najwyższy priorytet a `or` najniższy, więc `A and not B or C` jest ekwiwalentem `(A and (not B)) or C`. Jak zwykle można użyć nawiasów, aby wyrazić pożądaną kolejność kompozycji wyrażenia.

Argumenty operatorów boolowskich `and` i `or` są ewaluowane od lewej do prawej. Ewaluacja kończy się w momencie ustalenia wyniku. Na przykład, jeśli `A` i `C` są prawdą, ale `B` jest fałszem, `A and B and C` nie zewaluuje wyrażenia `C`. Przy użyciu ogólnej wartości, nie jako boolean, wartość zwracana tych operatorów to ostatnio ewaluowany argument.

Da się przypisać wynik porównania lub inne wyrażenie boolowskie do zmiennej. Na przykład,

    >>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
    >>> non_null = string1 or string2 or string3
    >>> non_null
    'Trondheim'

Zwróć uwagę, że w Pythonie, w przeciwieństwie do C, przypisanie wewnątrz wyrażeń musi być wyrażone bezpośrednio przez użycie walrus operatora `:=`. W ten sposób unikamy powszechnej klasy problemów spotykanych w programach C: wpisywania `=` w wyrażeniu, gdy miało się intencję wpisać `==`.

## 5.8. Porównywanie sekwencji i innych typów

Obiekty sekwencji zazwyczaj mogą być porównywane do innych obiektów, o tym samym typie sekwencji. Porównanie używa porządku leksykograficznego: pierwszy albo pierwsze dwa elementy są porównywane, i jeśli się różnią, to determinuje wynik porównania; jeśli są równe, następne dwa elementy są porównywane, i tak dalej, dopóki któraś z sekwencji się nie skończy. Jeśli dwa elementy do porównania same są sekwencjami tego samego typu, porównanie leksykograficzne odbywa się rekursywnie. Jeśli wszystkie elementy dwóch sekwencji są równe, takie sekwencje są traktowane jako równe. Jeśli jedna sekwencja jest początkową sub-sekwencją drugiej, ta krótsza sekwencja jest mniejszą. Porządek leksykograficzny ciągów znaków używa liczby tablicy Unicode do wyznaczenia porządku pojedynczych znaków. Niektóre przykłady porównań pomiędzy sekwencjami takiego samego typu:

    (1, 2, 3)              < (1, 2, 4)
    [1, 2, 3]              < [1, 2, 4]
    'ABC' < 'C' < 'Pascal' < 'Python'
    (1, 2, 3, 4)           < (1, 2, 4)
    (1, 2)                 < (1, 2, -1)
    (1, 2, 3)             == (1.0, 2.0, 3.0)
    (1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

Zwróć uwagę, że porównywanie obiektów innych typów przy użyciu `<` lub `>` jest dozwolone pod warunkiem, że te obiekty mają odpowiednie metody porównań. Na przykład mieszane typy numeryczne są porównywane w oparciu o ich wartość numeryczną, tak że 0 równa się 0.0 i tak dalej. W innych przypadkach, zamiast zwracać arbitralny porządek, interpreter zgłosi wyjątek `TypeError`.

Przypisy

[1]

Inne języki mogą zwracać zmieniony obiekt, co pozwala na łańcuchowanie metod, na przykład `d->insert("a")->remove("b")->sort();`.