BiteSize: Iterowanie po kilku listach – metoda zip i zip_longest

Jak iterować po wielu listach jednocześnie? W tym BiteSize zobaczymy metodę zip i zip_longest. Kod będzie bardziej Pythonowy i pozbędziemy się kilku znaczących problemów związanych z iterowaniem po indeksach.

Standardowe iterowanie

Niestety ten standard jest bardzo powszechny w Pythonie, szczególnie u osób, które pisały wcześniej w językach C-podobnych – czyli iterujemy po indeksach. Jest to jakieś rozwiązanie i działa, także, po co się czepiać?

Ponieważ istnieje bardziej Pythonowe rozwiązanie! A to bym chciał osiągnąć, żeby nasz kod był bardziej Pythonowy.

list_a = [1, 2, 3, 4, 5]
list_b = ["a", "b", "c", "d", "e"]

for i in range(len(list_a)):
    a = list_a[i]
    b = list_b[i]
    print(a, b)

Mówiąc bardzo szczerze, iterowanie po range(len(list_a)) tylko po to, aby wyciągać elementy z tej listy to jeden z najbrzydszych sposobów iterowania po liście w Pythonie.

Natomiast, pomijając ten wątek, jakie m.in. możemy napotkać problemy?

  • Liczba elementów w poszczególnych listach, może być różnażeby sobie z tym poradzić, musimy wybrać długości obu list, zweryfikować czy są takie same i ewentualnie wybrać, która długość nam pasuje bardziej
  • Co jak weźmiemy długość krótszej listy?Nie uzyskamy wszystkich elementów dłuższej listy, w sumie nie jest jeszcze najgorzej
  • Co jak trafimy na długość listy dłuższej?Dostaniemy IndexError, oczywiście poradzimy sobie z tym, no ale…

Dodatkowo w kontekście pętli for należy wyciągać informację z list, po których iterujemy, co zabiera nam kolejne linie kodu.

Wykorzystanie zip

Metodę zip nie musimy znikąd importować, jest ona wbudowana w język. Ten sam kod wykorzystując metodę zip:

list_a = [1, 2, 3, 4, 5]
list_b = ["a", "b", "c", "d", "e"]

for a, b in zip(list_a, list_b):
    print(a, b)

Czysty prawda?! 🙂

Okej, ale wróćmy do problemów z poprzedniego punktu. Nie przejmujemy się długością list, natomiast jak długo będziemy iterować? – Otóż metoda zip pod spodem, wybiera NAJKRÓTSZĄ listę i iterujemy, dopóki elementy tej listy się nie skończą.

Co z elementami listy dłuższej, które ominęliśmy? Po prostu zostały pominięte.

list_a = [1, 2, 3, 4, 5]
list_b = ["a", "b"]

for a, b in zip(list_a, list_b):
    print(a, b)

# Output:
# 1 a
# 2 b

Czemu więc wcześniej mówię, że to problem, a teraz nie? Zanim odpowiem na to pytanie zobaczmy jak działa zip_longest.

zip_longest w praktyce

Najpierw musimy zaimportować metodę z biblioteki standardowej itertools. Kod nie bardzo się zmieni, jedna lista jest dłuższa od drugiej i zamiast zip wykorzystamy zip_longest.

from itertools import zip_longest

list_a = [1, 2, 3, 4]
list_b = ["a", "b"]

for a, b in zip_longest(list_a, list_b, fillvalue="No element here :( "):
    print(a, b)

# Output:
# 1 a
# 2 b
# 3 No element here :( 
# 4 No element here :( 

Jaki będzie efekt? Tak jak wskazuje nazwa, iterujemy do najdłuższej listy (ang. longest). Natomiast tam, gdzie już nie ma elementów listy, zostanie wstawiona wartość, którą podałem w parametrze fillvalue, czyli tekst No element here :(. Oczywiście możemy tam wpisać cokolwiek, ale to zależy od naszego zadania. Domyślnie jest to None.

Dodatkowe przypadki

Iterowanie po dwóch listach pewnie nie robi na nikim wrażenia, a co jeżeli ma być ich więcej? Wtedy zamiast pisać:

for i in range(len(list_a)):
    a = list_a[i]
    b = list_b[i]
    c = list_c[i]
    d = list_d[i]
    e = list_e[i]
    print(a, b, c, d, e)

Możemy użyć ładniejszej (moim zdaniem) składni i napiszemy po prostu:

for a, b, c, d, e in zip(list_a, list_b, list_c, list_d, list_e):
    print(a, b, c, d, e)

Są też przypadki, kiedy nie chcemy rozpakowywać list do oddzielnych zmiennych, tylko trzymać je razem. Jedna grupa elementów pod tym samym indeksem, trafi do jednej tupli.

for my_tuple in zip(list_a, list_b, list_c):
    print(len(mytuple))
    print(my_tuple)

Podsumowanie

Zip oraz zip_longest są bardzo pomocne na co dzień w iterowaniu po listach, ale także liniach pliku czy innych iterable. Należy pamiętać czy zależy nam na tym, aby wybrać wszystkie elementy, nawet jeżeli nie mają swojego „zestawu” – wtedy wykorzystujemy zip_longest. Czy chodzi nam tylko o pełne informacje i wykorzystamy zip.

W każdym razie musimy przemyśleć, na czym nam zależy i na pewno wykorzystywać to co nam oferuje język w którym piszemy 🙂

Dzięki, że dotarłeś/aś do tego momentu. Jeżeli Ci się spodobało i wiesz już coś nowego, proszę udostępnij ten wpis i blog komuś, kto też na tym skorzysta 🙂 Więcej tego typu wpisów tutaj.

Zapraszam również na mojego Instagrama, na którym też publikować będę różne treści związane z Pythonem.

Pozdrawiam i do zobaczenia, usłyszenia i kodowania
~Marek

Scroll to Top