Pętle while oraz for to dwie najważniejsze rodzaje pętli w pythonie. Pętla while jest na tyle uniwersalna, że można użyć jej do zastąpienia pętli for, z kolei pętla for jest jedną z najczęściej wykorzystywanych. Czym różnią się one od siebie oraz czym tak w zasadzie jest pętla?

Pętla to konstrukcja, która pozwala powtarzać jakieś działanie. Instrukcja while służy do tworzenia uniwersalnych pętli, z kolei instrukcja for służy do przechodzenia przez elementy sekwencji lub dowolnego obiektu iterowalnego wraz z wykonywaniem określonych działań dla każdego z napotkanych elementów. Do sterowania pętlami stosuje się instrukcje takie jak break oraz continue. Istnieją również pewne wbudowane funkcje, które są zazwyczaj wykorzystywane z pętlami, a należą do nich funkcja zip(), range() oraz map().

Pętla while

Instrukcja while jest najbardziej uniwersalną  pętlą w pythonie. Wykonuje ona blok kodu, tzw. ciało pętli, do momentu, do którego warunek umieszczony w jej nagłówku zwraca wartość będącą prawdą. Jeśli test warunku zwróci wartość będącą fałszem, wówczas sterowanie wraca z powrotem do kodu napisanego poza blokiem while. Jeśli test umieszczony w nagłówku zwraca fałsz już przy pierwszej iteracji, wówczas ciało pętli nigdy nie zostanie wykonane.

Forma pętli while

Pętlę while tworzy się w następujący sposób:

while warunek:

  instrukcja1

else:

  instrukcja2

Podobnie jak w przypadku instrukcji if, instrukcja else jest opcjonalna. Blok kodu następujący po else zostanie wykonany wtedy, gdy pętla nie skończyła się za pomocą instrukcji break. To sprawia, że instrukcja else jest często nazywaną instrukcją 'if no break’.

Uwaga! Zastosowanie pętli while wymaga tego, aby w ciele pętli modyfikowane było wyrażenie lub zmienna znajdująca się w warunku – innymi słowy warunek musi w końcu okazać się fałszem! Jeśli nie dojdzie do zmiany warunku, przez co będzie on zawsze prawdą, wówczas pętla będzie nieskończona.

Żeby zakończyć działanie takiej pętli w terminalu należy wcisnąć kombinację klawiszy Ctrl+C:

>>> while 1: print("true")

...

true

true

true

true

true

true

true

true

true

...

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

KeyboardInterrupt

Przykładowe użycie pętli while z łańcuchem znaków w nagłówku:

>>> test="Python"

>>> while test:

... print(test)

... test=test[:-1]

...

Python

Pytho

Pyth

Pyt

Py

P

Z każdą iteracją zmieniamy wartość zmiennej test wykorzystaną w nagłówku w ten sposób, aby w końcu ’ wyczerpać’ łańcuch znaków, czyli po to, aby warunek zwrócił w końcu fałsz. Dzięki temu dojdzie do przerwania pętli. Innym popularnym sposobem na kierowanie pętlą while jest wykorzystanie licznika, którego wartość zmienia się z każdym wykonaniem pętli.

>>> x = 10

>>> y = 0

>>> while y < x:

... print(y, end=' ')

... y+=1

...

0 1 2 3 4 5 6 7 8 9 >>>

Instrukcja break, continue i else

Jak już powiedzieliśmy wcześniej do kontrolowania pętli wykorzystywane są instrukcje break oraz continue, które stanowią instrukcję zagnieżdżone. Z instrukcją break łączy się również instrukcja else, która nie jest typowa jedynie dla instrukcji warunkowych. Zachowanie poszczególnych instrukcji można opisać następująco:

  • break – wyjście z pętli, czyli przekazanie sterowania do kolejnego bloku kodu następującego po danej pętli,
  • continue – powrót do nagłówka pętli,
  • else – wykonywane tylko wtedy, gdy pętla zakończy się bez natrafienia na instrukcję break. 

Ogólna konstrukcja wygląda zatem następująco:

while warunek1:

  instrukcje1

  if warunek2: break

  if warunek3: continue

else:

instrukcje2

Instrukcje break i continue mogą pojawić się w dowolnym miejscu w ciele pętli, jednak zazwyczaj łączą się z instrukcją warunkową if.

Instrukcja continue w pętli

Instrukcja continue powoduje natychmiastowe przeskoczenie na górę pętli. Pozwala to wykonywać tak zwane skoki w programie, służące do na przykład omijania pewnych wartości. W ten sposób pętle nie muszą być aż tak zagnieżdżone. Instrukcję można wykorzystać na przykład do wypisania wszystkich liczb parzystych dla danego zakresu:

>>> x = 6

>>> while x:

... x = x - 1

... if x % 2 != 0: continue

... print(x)

...

4

2

0

W tym przypadku należy pamiętać o tym, aby instrukcję zmieniającą warunek umieścić na początku. Inaczej może dojść do powstania nieskończonej pętli, gdy wartość x za drugim wykonaniem wynosi 5, sterowanie trafia do instrukcji continue, przez co wartość x nie zostaje zmieniona.

Instrukcja break w pętli

Instrukcja break służy do natychmiastowego wyjścia z pętli. Kod znajdujący się po instrukcji break w ciele pętli nigdy nie zostanie wykonany.

>>> x = 10

>>> while x:

... print(x)

... x = x - 1

... if x == 5: break

...

10

9

8

7

6

Instrukcja else w pętli

Instrukcja else w pętli jest wykonywana wtedy, gdy w ciele pętli nie dojdzie do wywołania instrukcji break. Jest ona wykonywana także wtedy, gdy kod z ciała pętli nie zostanie wykonane w ogóle, ponieważ wtedy również nie dochodzi do wykonania instrukcji break. Else w pętli jest wykonywane również wtedy, gdy warunek nagłówku jest od początku fałszem.

>>> y = 100

>>> while y > 1:

...   if y % 5 == 0:

...     print(y, 'jest liczbą podzielną przez 5')

...     break

...   y = y - 1

... else:

...   print(y, 'nie jest liczbą podzielną przez 5')

...

100 jest liczbą podzielną przez 5

W przykładzie powyżej nie doszło do wykonania instrukcji else, jako że doszło do wykonania instrukcji break.

>>> y = 9

>>> while y > 1:

... if y % 10 == 0:

... print(y, 'jest podzielne przez 10')

... break

... y = y - 1

... else:

... print(y, 'nie jest podzielne przez 10')

...

1 nie jest podzielne przez 10

Kod nie napotkał na break, a więc doszło do wykonania kodu z klauzuli else. Klauzula else znacząco ułatwia pisanie pętli, jako że pozwala uniknąć dodatkowego zagnieżdżenia.

Przykładowo, jeśli chcemy wiedzieć, czy dana wartość została znaleziona w sekwencji możemy skorzystać z poniższego kodu:

>>> found = False

>>> match = 'abcd'

>>> while match:

... if 'z' in match:

... print('Znaleziono z')

... found = True

... else:

... match = match[1:]

... if not found:

... print('Nie znaleziono z')

...

Nie znaleziono z

Nie znaleziono z

Nie znaleziono z

Nie znaleziono z

Albo możemy użyć znacznie prostszej struktury, w której wykorzystujemy tylko jedną instrukcję if:

>>> found = False

>>> match = 'abcd'

>>> while match:

... if 'z' in match:

... print('Znaleziono z')

... found=True

... match=match[1:]

... else:

... print('Nie znaleziono z')

...

Nie znaleziono z

Pętla for

Pętla for w Pythonie jest stosowana do przechodzenia przez kolejne elementy w sekwencji uporządkowanej lub w innym obiekcie literowalnym. Działa na łańcuchach znaków, listach, kropkach, a nawet plikach. Ogólny format pętli wygląda następująco:

for element in obiekt:

  instrukcja1

else:

  instrukcja2

Do każdego elementu obiektu będącego sekwencją, w kolejności od prawego do lewego elementu, przypisywana jest tymczasowa zmienna – tutaj określona jako 'element’, ale tak naprawdę może ona mieć dowolną nazwę. Element, czy też tymczasową zmienną przypisywaną do każdego elementu sekwencji, można potraktować jako kursor przychodzący przez sekwencję. W ciele funkcji jego nazwa może zostać zmieniona, jednak pętla for automatycznie przywróci zmienną o oryginalnej nazwie, po czym ustawi ją na kolejny element, gdy sterowanie wróci do góry pętli. Gdy pętla skończy działanie, wówczas zmienna tymczasowa przyjmuje wartość przypisaną do elementu w ostatniej interakcji, jeśli tylko działanie pętli nie zostało zakończone poprzez instrukcję break.

Pętla for z break i continue

W instrukcji for również można zastosować opcjonalny blok else, który działa tak samo jak w pętli while – zawarty w nim kod jest wykonywany tylko wtedy, gdy pętla nie natrafi na instrukcję break. W pętli for również można zastosować instrukcję continue i zachowa się ona podobnie jak w pętli while, czyli wymusi przejście na górę pętli.

Przykład:

>>> for x in 'Python':

... print(x, end=' ')

...

P y t h o n >>>

W tym przypadku zmienna x odnosi się do każdego kolejnego elementu sekwencji, czyli łańcucha znaku. Pętle for można wykorzystać także do wykonania pewnych operacji matematycznych, Jeśli na przykład nie chcemy korzystać z wbudowanej funkcji sumowania:

>>> sum = 0

>>> for x in [1, 2, 3, 4, 5]:

... sum += x

...

>>> sum

15

Pętla for i przypisanie rozpakowujące

W pętli for można wykorzystać przypisanie rozpakowujące, jeśli tylko znana jest nam struktura danych w wybranym zbiorze. Wygląda to następująco:

>>> my_tuples = [(1, 2), (3, 4)]

>>> for (x, y) in my_tuples:

... print(x, y)

...

1 2

3 4

W każdym przejściu zmienna x, y jest przypisywana do kolejnej kropki znajdującej się w tablicy my_tuples.

Iteracja po kluzczach słownika (i innych typach sekwencji) za pomocą pętli for i krotek

Krotki w pętli for często wykorzystuje się do literacji po kluczach i wartościach słowników do rozpakowania wyników zwracanych przez metodę items():

>>> D = {'key_one': 1, 'key_two': 2}

>>> for (key, value) in D.items():

... print(key, '--->', value)

...

key_one ---> 1

key_two ---> 2

Pętla for pozwala również wyodrębnić dane zagnieżdżone, a więc rozpakować bardziej skomplikowane struktury:

>>> for (a, b, (c)) in [(1, 2, (3)), (4, 5, (6))]: print(a)

...

1

4

W ten sposób uzyskujemy dostęp do pojedynczej  zmiennej, w tym przypadku jest to a.

Rozpakowywać można wiele różnych, niejednorodnych sekwencji, jak na przykład:

>>> for (a, b, (c)) in [(1, 2, (3)), ['de', 7, 3]]: print(a)

...

1

de

Pętlę for można wykorzystać również przy pisaniu rozpakowującym, czyli z gwiazdką. W praktyce jest to często wykorzystywane do wybierania kilku kolumn z wierszy danych, aby można było przedstawić je jako sekwencję zagnieżdżone:

>>> for a, *b, c in [(1, 2, 3, 4), (5, 6, 7, 8)]: print(b)

...

[2, 3]

[6, 7]

Zagnieżdżone pętle for

Pętle for można zagnieżdżać, jeśli na przykład chce się wykonywać dwie lub więcej iteracji jednocześnie. Wówczas w tym samym czasie wykonywane są dwie pętle – jest to najczęściej pętla zewnętrzna, która przegląda listę elementów głównych, a następnie dla każdego z elementów głównych przeszukiwana jest kolejna sekwencja zawierająca zwykłe elementy.

Załóżmy, że mamy dwie listy:

>>> first_list = ['python', 'abc', 1]

>>> second_list = ['ccc', 345, 788, 'xyz', 'abc', 34, 6660]

Teraz chcemy sprawdzić, czy któryś z elementów znajdujących się w drugiej liście znajduje się również w liście pierwszej. Możemy tego dokonać za pomocą zagnieżdżonej pętli for:

>>> for item1 in first_list:

... for item2 in second_list:

... if item1 == item2:

... print("Znaleziono")

... else:

... print("Nie znaleziono")

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Nie znaleziono

Jak widzimy pętla for została wykonana aż 21 razy (7 razy 3), czyli dla każdego elementu z listy pierwszej wyszukiwanie zostało przeprowadzone tyle razy, ile jest elementów w liście drugiej. Oczywiście nie jest to najprostszy sposób na znalezienie wspólnych elementów w obu listach. O wiele łatwiej jest skorzystać z instrukcji warunkowej if oraz operatora in, który niejawnie przeszukuje sekwencję.

>>> for item in first_list:

... if item in second_list:

... print("Znaleziono")

... else:

... print("Nie znaleziono")

...

Nie znaleziono

Znaleziono

Nie znaleziono

Wspólna część sekwencji

Typowym zastosowaniem pętli for w Pythonie jest wyznaczanie wspólnych części dwóch sekwencji. Można tego dokonać przy użyciu pętli for, instrukcji warunkowej if oraz listy wraz z metodą append():

>>> string1 = 'Python'

>>> string2 = 'pYTHoN'

>>> res = []

>>> for let in string1:

... if let in string2:

... res.append(let)

...

>>> res

['o']

Pętla for do odczytywania zawartości plików

Pęta for świetnie nadaje się do odczytywania zawartości plików, czyli jako tak zwany skaner plików. Pliki składają się z w końcu z wielu wierszy, które niekoniecznie chcemy wszystkie ładować do pamięci komputera. W tym celu można skorzystać z pętli for w następujący sposób:

>>> for line in open('test.txt', 'r'):

... print(line)

...

test

Skorzystanie z powyższej formy pętli for, czyli jako skanera pliku, to najlepszy sposób na czytanie jego zawartości, ponieważ jest on prosty, a także może działać dla dowolnie dużych plików. Nie ładuje bowiem całego pliku na raz do pamięci. Jeśli jednak jesteśmy pewni co do tego, że plik zmieści się w pamięci komputera, możemy skorzystać również z innej metody:

>>> for line in open('test.txt', 'r').readlines():

... print(line)

...

test

W praktyce metoda readlines() ładuje cały plik wiersz po wierszu do listy łańcuchów znaków. Korzystanie z niej jest przydatne, jeśli na przykład chcemy od razu przeprowadzić jakąś operację na danym pliku – będzie to możliwe, jako że listy mogą być modyfikowane w miejscu.

Dlaczego nie należy liczyć elementów w Pythonie ręcznie? Pętle for i wyspecjalizowane iteracje

Z racji tego, że w Pythonie istnieje pętla for, z reguły nie powinniśmy ręcznie liczyć na przykład elementów listy. Jednak istnieją sytuację, w których chcemy wykorzystać pętlę for w bardziej wyspecjalizowany sposób, na przykład jeśli chcemy dokonywać modyfikacji elementów listy lub przychodzić tylko co drugi czy co trzeci jej element. W celu dokonywania takich właśnie wyspecjalizowanych iteracji korzysta się z wbudowanych funkcji: range(), zip(), enumerate() oraz map().

Funkcja range()

Wbudowana funkcja range() służy do generowania indeksów dla pętli for. Jej działanie różni się znacząco w wersji Pythona 2.x oraz 3.x. We wcześniejszych wersjach funkcja zwracała listę, natomiast w nowszych wersjach jest iteratorem, który generuje elementy na żądanie, czyli działa podobnie do generatora. Z tego względu w celu uzyskania wszystkich wyników na raz należy umieścić ją w funkcji list().

>>> list(range(8))

[0, 1, 2, 3, 4, 5, 6, 7]

Jak można się domyślić funkcja ta generuje listę liczb całkowitych od zera do wskazanej liczby, bez uwzględnienia niej samej. Funkcje można wywołać z dwoma, a nawet trzema argumentami.

>>> list(range(3, 8))

[3, 4, 5, 6, 7]
  • Dwa argumenty: pierwszy jest interpretowany jako dolna granica 
  • Trzy argumenty: pierwszy jest interpretowany jako dolna granica, natomiast trzeci jako krok, który jest dodawany do każdej kolejnej liczby całkowitej wyniku.
>>> list(range(3, 8, 2))

[3, 5, 7]

Funkcja range świetnie sprawdza się wtedy, gdy chce się powtórzyć określone działanie pewną liczbę razy. Na przykład w celu wypisania  licznika:

>>> for i in range(3):

... print(i, 'Python')

...

0 Python

1 Python

2 Python

Pętla for wymusza na funkcji range() generowanie wyników, dlatego nie trzeba korzystać z funkcji list.

Kiedy powinno się korzystać z funkcji range() w pętli for?

Jak wspomnieliśmy wcześniej w Pythonie należy powstrzymać się od ręcznego liczenia elementów sekwencji. Pętla for automatycznie przechodzi przez obiekt iterowalny, a jeśli chcemy poznać długość iterowanej sekwencji, wystarczy skorzystać z funkcji len(). Jest jednak przypadek, w którym należy skorzystać z funkcji range połączonej z funkcją len() – dotyczy to sytuacji, w której chcemy w miejscu dokonać modyfikacji danej sekwencji. Porównaj przykłady:

>>> L = [1, 2, 3, 4]

>>> for x in L:

... x += 1

...

>>> L

[1, 2, 3, 4]

Żaden z elementów listy nie uległ zmianie – iteracja przechodzi przez elementy listy, a nie pozycje przesunięcia. Żeby zmodyfikować elementy w miejscu należy skorzystać z poniższego sposobu:

>>> for x in range(len(L)):

... L[x] = L[x] + 1

...

>>> L

[2, 3, 4, 5]

Alternatywę dla tego typu operacji stanowi lista składana, jednak w tym przypadku zwraca ona nową listę:

>>> L = [1, 2, 3, 4]

>>> new_L = [x + 1 for x in L]

>>> new_L

[2, 3, 4, 5]

Funkcja range() w połączeniu z funkcją len() służy również do przeprowadzania zmiany na sekwencjach, w tym na przykład ich dzielenia lub też tasowania. Spójrz na poniższy przykład:

>>> my_string = 'testing'

>>> for x in range(len(my_string)):

... res = my_string[x:] + my_string[:x]

... print(res)

...

testing

estingt

stingte

tingtes

ingtest

ngtesti

gtestin

Każda literacja działa w taki sam sposób, dochodzi jedynie do przesunięcia miejsca wycinka. Wartość iterowanej zmiennej nie ulega zmianie.

Funkcja zip()

Funkcja zip() umieszczona w pętli for umożliwia równoległe przechodzenie przez większą liczbę sekwencji. Funkcja przyjmuje jako argument jedną lub większą liczbę sekwencji, a następnie zwraca serię krotek, w których połączone zostały pary równoległych elementów z każdej sekwencji.

>>> L = [1, 3, 5]

>>> L2 = [2, 4, 6]

>>> list(zip(L, L2))

[(1, 2), (3, 4), (5, 6)]

Do wywołania wszystkich wyników funkcji zip(), która zwraca obiekt iterowalny, należy wykorzystać wywołanie listy. W pętli for nie jest to konieczne:

>>> for (x, y) in zip(L, L2):

... print(x, y, '->', x+y)

...

1 2 -> 3

3 4 -> 7

5 6 -> 11

Funkcja zip przyjmuje dowolny typ sekwencji, a także może przyjąć więcej niż dwa argumenty:

>>> L3 = [10, 10, 10]

>>> print(list(zip(L, L2, L3)))

[(1, 2, 10), (3, 4, 10), (5, 6, 10)]

Długość argumentów nie musi być taka sama, jeśli się różni, wówczas funkcja odetnie nadprogramowe elementy w ten sposób, aby dopasować się do długości najkrótszej sekwencji.

>>> L3 = [11, 11]

>>> print(list(zip(L, L2, L3)))

[(1, 2, 11), (3, 4, 11)]

Funkcja zip() do tworzenia słowników

Funkcję zip można wykorzystać do tworzenia słowników. Wystarczy stworzyć listę kluczy oraz listę wartości, a następnie połączyć je ze sobą. Umożliwia to tworzenie słowników w sposób dynamiczny:

>>> keys = ['Ala', 'ma', 'kota']

>>> values = [1, 2, 3]

>>> my_dict = {}

>>> for (k, v) in zip(keys, values): my_dict[k] = v

...

>>> print(my_dict)

{'Ala': 1, 'ma': 2, 'kota': 3}

Pętla for przypisuje do każdej krotki będącej wynikiem działania funkcji zip() dwie zmienne, jednocześnie przeprowadzając iterację. Następnie z każdą iteracją tworzy nowy rekord w słowniku. W jeszcze prostszej formie wygląda to następująco:

>>> my_dict2 = dict(zip(keys, values))

>>> my_dict2

{'Ala': 1, 'ma': 2, 'kota': 3}

W nowszych wersjach Pythona możliwe jest ominięcie pętli for poprzez przekazanie krotek kluczy i wartości złączonych za pomocą funkcji zip().

Funkcja enumerate()

Kolejna funkcja wbudowana, która mniej lub bardziej pośrednio łączy się z pętlą for, to funkcja enumerate. Pozwala ona uzyskać dostęp do licznika bez konieczności jego ręcznego kontrolowania:

>>> my_str = 'Python'

>>> for (offset, c) in enumerate(my_str):

... print(offset, ' to pozycja przesunięcia dla litery ', c)

...

0 to pozycja przesunięcia dla litery P

1 to pozycja przesunięcia dla litery y

2 to pozycja przesunięcia dla litery t

3 to pozycja przesunięcia dla litery h

4 to pozycja przesunięcia dla litery o

5 to pozycja przesunięcia dla litery n

W następnym wpisie przyjrzymy się bliżej funkcjom.