article banner (priority)

List comprehensions w Pythonie

Pętla for nie służy tylko do iterowania po range. Wręcz przeciwnie. Znacznie częstsze jest używanie jej do iterowania po liście albo tuple.

names = ["Ala", "Basia", "Celina"]
for name in names:
    print(name)

# Wypisze:
# Ala
# Basia
# Celina

Gdy już jednak wspominamy o iterowaniu po listach, warto zgłębić bardzo przydatną funkcjonalność, jaką są listy składane.

Listy składane

Mniej znaną, a bardzo przydatną funkcjonalnością języka Python są listy składane1. Pozwala ona na utworzenie nowej listy, na podstawie istniejącej listy lub zakresu, ale po dokonaniu pewnych modyfikacji.

Najważniejszą częścią listy składanej jest generator. Określa on, co powinno znajdować się na nowej liście. Przykładowy generator to x * 10 for x in numbers, co przetłumaczylibyśmy, że chcemy x * 10 dla każdego x z numbers. Taki generator będzie zwracał kolejne elementy z numbers, pomnożone przez 10. Jeśli otoczymy go nawiasem kwadratowym, to uzyskamy listę z tymi wartościami.

numbers = [x + 1 for x in range(5)]
print(numbers)  # [1, 2, 3, 4, 5]
numbers = [x * 10 for x in range(5)]
print(numbers)  # [0, 10, 20, 30, 40]

Analogicznie możemy modyfikować stringi. Możemy na przykład użyć metody upper, by imiona na nowej liście zaczynały się wielkimi literami.

names = ["Ada", "Basia", "Celina"]
names_upper = [name.upper() for name in names]
print(names_upper)  # ['ADA', 'BASIA', 'CELINA']

Zauważ, że identyczny efekt moglibyśmy uzyskać przy pomocy pętli for oraz nowej listy.

new_list = [transformation(x) for x in old_list]

# Można uzyskać poprzez:
new_list = []
for x in old_list:
    new_list.append(transformation(x))

W praktycznych przypadkach często używamy tej funkcjonalności, by przetransformować listę obiektów na nową listę. Jeśli na przykład mielibyśmy listę lekarzy, ale interesowałyby nas wyłącznie ich imiona, moglibyśmy użyć listy składanej, by te imiona wyciągnąć.

doctor_names = [doctor.name for doctor in doctors]

Lista składana z warunkiem

Kolejną możliwością jest dodanie warunku, który powinien być spełniany, by elementy mogły się znaleźć na nowej liście. Warunek ten dodajemy po słówku if, na końcu generatora. W warunku możemy używać nazwy zmiennej określonej pomiędzy forin. Przykładowo, jeśli spośród listy imion chcielibyśmy wybrać tylko te, które zaczynają się na literę "M", to moglibyśmy to zrobić następująco:

names = ["Jola", "Marcin", "Kamil", "Maja", "Stefan"]
m_names = [name for name in names if name[0] == "M"]
print(m_names)  # ['Marcin', 'Maja']

Dodanie warunku nie przeszkadza nam zastosować omawianego wcześniej przekształcenia.

names = ["Jola", "Marcin", "Kamil", "Maja", "Stefan"]
m_names = [name.upper() for name in names
           if name[0] == "M"]
print(m_names)  # ['MARCIN', 'MAJA']

Ponownie moglibyśmy uzyskać analogiczny wynik, używając pętli for i nowej kolekcji. Teraz jednak musielibyśmy dostawić warunek if.

new_list = [transformation(x) for x in old_list
            if condition(x)]

# Można uzyskać poprzez:
new_list = []
for x in old_list:
    if condition(x):
        new_list.append(transformation(x))

Tworzenie tuple przez generatory

Warto dodać, że tuple możemy również przetwarzać przy użyciu tego zapisu. Tutaj generator powinien się znaleźć w konstruktorze tuple. Pozostałe funkcjonalności pozostają takie same.

names = ("Jola", "Marcin", "Kamil", "Maja", "Stefan")
names = tuple(name for name in names if name[0] == "M")
print(names)  # ('Marcin', 'Maja')
print(type(names))  # <class 'tuple'>

Ćwiczenie: Iterowanie i listy składane

Dla poniższej zmiennej names:

  • stwórz listę, w której wszystkie te imiona zaczynają się wielką literą (przypomnij sobie metody z klasy str),
  • stwórz tuple, w którym znajdują się wyłącznie żeńskie imiona, zaczynające się wielką literą (żeńskie imiona to te kończące się na "a"),
  • policz sumaryczną długość wszystkich tych imion (długość stringa sprawdzamy funkcją len, pamiętaj, że możesz użyć pętli i w niej dodawać wartości do zmiennej).
names = ["michał", "nela", "ola", "przemek"]

Odpowiedzi na końcu książki.

1:

"listy składane" to polskie tłumaczenie angielskiego pojęcie "list comprehension". Niektóre książki tłumaczą to pojęcie jako "wyrażenie listowe", ale jest to błędne, gdyż "wyrażenie listowe" obejmuje znaczeniowo znacznie więcej (w tym np. tworzenie listy przez nawias kwadratowy). Inne źródła używają pojęcia "odwzorowywanie list", które to obejmować powinno dużo mniej (wyłącznie mapowanie). Spotkałem się też z pojęciem "wytworniki list", które ciężko nawet skomentować, gdyż jedyną definicję słowa "wytwornik" jaką znalazłem to "urządzenie do emisji gazów".