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.1.5. Rozpakowywanie w listach i list comprehensions
-----------------------------------------------------

Sekcja dotycząca Rozpakowywanie listy argumentów opisuje użycie "*" do
"rozpakowywania" elementów iterowalnego obiektu, dostarczając każdy z
nich osobno jako argument do funkcji. Rozpakowywanie może być również
używane w innych kontekstach, na przykład podczas tworzenia list.
Podczas określania elementów listy, poprzedzenie wyrażenia znakiem "*"
spowoduje rozpakowanie wyniku tego wyrażenia, dodając każdy z jego
elementów do tworzonej listy:

   >>> x = [1, 2, 3]
   >>> [0, *x, 4, 5, 6]
   [0, 1, 2, 3, 4, 5, 6]

This only works if the expression following the "*" evaluates to an
iterable object; trying to unpack a non-iterable object will raise an
exception:

   >>> x = 1
   >>> [0, *x, 2, 3, 4]
   Traceback (most recent call last):
     File "<python-input-1>", line 1, in <module>
       [0, *x, 2, 3, 4]
   TypeError: Value after * must be an iterable, not int

Unpacking can also be used in list comprehensions, as a way to build a
new list representing the concatenation of an arbitrary number of
iterables:

   >>> x = [[1, 2, 3], [4, 5, 6], [], [7], [8, 9]]
   >>> [*element for element in x]
   [1, 2, 3, 4, 5, 6, 7, 8, 9]

Note that the effect is that each element from "x" is unpacked.  This
works for arbitrary iterable objects, not just lists:

   >>> x = [[1, 2, 3], 'cat', {'spam': 'eggs'}]
   >>> [*element for element in x]
   [1, 2, 3, 'c', 'a', 't', 'spam']

But if the objects in "x" are not iterable, this expression would
again raise an exception.


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, 'hello!'
   >>> t[0]
   12345
   >>> t
   (12345, 54321, 'hello!')
   >>> # Tuples may be nested:
   >>> u = t, (1, 2, 3, 4, 5)
   >>> u
   ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
   >>> # Tuples are immutable:
   >>> t[0] = 88888
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: 'tuple' object does not support item assignment
   >>> # but they can contain mutable objects:
   >>> v = ([1, 2, 3], [3, 2, 1])
   >>> v
   ([1, 2, 3], [3, 2, 1])
   >>> # they support unpacking just like lists:
   >>> x = [1, 2, 3]
   >>> 0, *x, 4
   (0, 1, 2, 3, 4)

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'}

Similarly to list comprehensions, set comprehensions are also
supported, including comprehensions with unpacking:

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

   >>> fruits = [{'apple', 'avocado', 'apricot'}, {'banana', 'blueberry'}]
   >>> {*fruit for fruit in fruits}
   {'blueberry', 'banana', 'avocado', 'apple', 'apricot'}


5.5. Słowniki
=============

Innym przydatnym typem danych wbudowanym w Pythona jest *słownik*
(patrz Mapping types --- dict, frozendict). 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}

And dictionary unpacking (via "**") can be used to merge multiple
dictionaries:

   >>> odds = {i: i**2 for i in (1, 3, 5)}
   >>> evens = {i: i**2 for i in (2, 4, 6)}
   >>> {**odds, **evens}
   {1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36}

   >>> all_values = [odds, evens, {0: 0}]
   >>> {**i for i in all_values}
   {1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36, 0: 0}

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();".
