For vs list comprehension vs map

Cześć,

dziś wezmę na tapet trzy elementy języka Python:

  • pętlę for,
  • list comprehension,
  • funkcję map.

Pomierzymy sobie ich efektywność w dwóch sytuacjach i na koniec wyciągniemy wnioski, odpowiadając na pytanie: co jest szybsze: pętla for, list comprehension czy funkcja map? Zapraszam!

Sytuacja pierwsza: wynik jest nam potrzebny

Wyobraźmy sobie, że działamy pętlą po jakiejś kolekcji, na jej elementach wykonujemy funkcję i wynik tej funkcji potrzebujemy zachować. Może to być np. wygenerowanie potęg liczb od 0 do 99. Możemy to zrobić na trzy sposoby:

Za pomocą standardowej pętli for:

1
2
3
numbers = []
for x in range(100):
  numbers.append(x * x)

Następnie z użyciem starej, dobrej listy składanej:

1
numbers = [x * x for x in range(100)]

I na koniec – funkcji map:

1
numbers = list(map(lambda x: x * x, range(100)))

Aby była pewność, że każde z tych podejść da ten sam efekt, dorzucam kawałek kodu, który to potwierdza:

1
2
3
4
5
6
7
8
9
numbers = []
for x in range(100):
  numbers.append(x * x)
 
numbers2 = [x * x for x in range(100)]

numbers3 = list(map(lambda x: x * x, range(100)))

print(numbers == numbers2 == numbers3)

Jego rezultatem jest oczywiście „True”.

Jeżeli ten sam rezultat można uzyskać na trzy różne sposoby, to który wybrać? Zmierzmy to!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from timeit import timeit

code1 = """
numbers = []
for x in range(100):
  numbers.append(x * x)
"""


code2 = """
numbers = [x * x for x in range(100)]
"""


code3 = """
numbers = list(map(lambda x: x * x, range(100)))
"""



print("For loop:", timeit(code1, number=100000))
print("List comp:", timeit(code2, number=100000))
print("Funkcja map:", timeit(code3, number=100000))

Wynik pomiaru jest dość ciekawy:

For loop: 1.0205242090160027
List comp: 0.6673786389874294
Funkcja map: 1.0319141389336437

Tę kategorię wygrywa, przeciwników zostawiając w tyle, podejście z użyciem list comprehensions!


Czekaj, stop!

Podoba Ci się to co tworzę? Jeśli tak to zapraszam Cię do zapisania się na newsletter:

Aby potwierdzić swoją subskrypcję, odbierz pocztę i kliknij w link potwierdzający:) jeśli maila nie ma to poczekaj chwile i/lub sprawdź folder spam/inne/oferty itp :)

Aby potwierdzić swoją subskrypcję, odbierz pocztę i kliknij w link potwierdzający:) jeśli maila nie ma to poczekaj chwile i/lub sprawdź folder spam/inne/oferty itp :)
a w ramach prezentu otrzymasz całkowicie za darmo, dwa dokumenty PDF „6 (nie zawsze oczywistych) błędów popełnianych podczas nauki programowania” który jest jednym z efektów ponad siedmioletniej pracy i obserwacji rozwoju niejednego programisty oraz „Wstęp do testowania w Pythonie”, będący wprowadzeniem do biblioteki PyTest.
Jeśli to Cię interesuje to zapraszam również na swoje social media.

Jak i do ewentualnego postawienia mi kawy :)

Sytuacja druga: wynik nie jest nam potrzebny

Nasze podejście trochę się zmienia, gdy zmienia się sytuacja w której używamy tych elementów. W poprzednim przykładzie wynik przetwarzania miał być nam potrzebny w dalszej części programu. Teraz zakładamy, że nie jest on mam potrzebny. Mamy znów trzy podejścia. Kolejno z pętlą for, listą składaną oraz funkcją map:

1
2
for x in numbers:
  mock(x)
1
[mock(x) for x in numbers]
1
list(map(mock, numbers))

Tutaj słowo wyjaśnienia – funkcja map tworzy generator i nie uruchomi naszej funkcji, dopóki nie będzie ku temu okazji. Okazję wymuszamy konwersją na listę.

Skoro mamy zebrane rzeczy do analizy, to tak jak wcześniej, zmierzmy to!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from timeit import timeit


setup = """
def mock(x):
  return x * x

numbers = range(100)
"""


code1 = """
for x in numbers:
  mock(x)
"""


code2 = """
[mock(x) for x in numbers]
"""


code3 = """
list(map(mock, numbers))
"""


print("For loop:", timeit(code1, number=100000, setup=setup))
print("List comp:", timeit(code2, number=100000, setup=setup))
print("Funkcja map:",timeit(code3, number=100000, setup=setup))

Rezultaty pomiaru:

For loop: 0.9080528659978881
List comp: 1.028726649004966
Funkcja map: 0.8416485550114885

Tutaj nie ma już jednogłośnego zwycięzcy. Niby funkcja map wygrywa, ale jest tylko troszkę szybsza niż pętla for.

Podsumownie

Wnioski nasuwają się same, prawda? Jeśli potrzebujesz pracować na danych w dalszej części programu, to używaj listy składanej. Jeśli nie ma potrzeby pracy na wyniku przetwarzania, a samo uruchomienie funkcji jest wystarczające to… To tu nie jest tak jednoznacznie. To, co bym polecał, to jednak używać pętli for. Różnica jest niewielka, a czytelność znacznie lepsza.

Oczywiście nie wszystkie pętle jesteśmy w stanie zapisać z użyciem list składanych czy zapakować do funkcji map. Przykładowo, jeśli Twoja pętla posiada słówka break lub continue, to może być ciężko.

Dzięki za wizytę,
Mateusz Mazurek
Mateusz M.

Pokaż komentarze

    • Każdy ma jakieś podejście które sobie wyrobił na przestrzeni czasu:) i myślę że to jest spoko:)

  • Bardzo fajny artykuł.

    Dla mnie map było mało zrozumiałe do czasu aż z JS przerzuciłem się na Pythona. W JS klepałem najczęściej pętle for do wszystkiego. W starszej wersji JS map chyba nie było, natomiast w nowszej wersji JS funkcja map już doszła oraz pojawiło się też bardzo dużo nowych elementów.

    Ten artykuł trochę mi przypomina artykuł na moim blogu o optymalizacji kodu w Pythonie.

    Pozdrawiam :-).

  • Wiadomo czemu tak zachowują się polecenia? Czemu w przypadku pierwszym jest az taka różnica np.

    • Nie podglądałem bytecode'u, a pewnie tam znajduje się odpowiedź :D możliwe że kiedyś podejrzę i zrobię osobny wpis. Jeśli mnie uprzedzisz, to będę super wdzięczny, jeśli podzieliłbyś się wnioskami w komentarzy, znacząco rozszerzyło by to zakres artykułu:)

Ostatnie wpisy

Podsumowanie: maj, czerwiec, lipiec i sierpień 2024

Oj daaawnoo mnie tu nie było. Ale wakacje to był czas dużej liczby intensywnych wyjazdów i tak naprawdę, dopiero jakoś… Read More

2 miesiące ago

Podsumowanie: kwiecień 2024

Cześć! Zapraszam na krótkie podsumowanie kwietnia. Wyjazd do Niemiec A dokładniej pod granicę z Francją. Chrześnica miała pierwszą komunię. Po… Read More

6 miesięcy ago

Podsumowanie: luty i marzec 2024

Ostatnio tygodnie były tak bardzo wypełnione, że nie udało mi się napisać nawet krótkiego podsumowanie. Więc dziś zbiorczo podsumuję luty… Read More

7 miesięcy ago

Podsumowanie: styczeń 2024

Zapraszam na krótkie podsumowanie miesiąca. Książki W styczniu przeczytałem "Homo Deus: Historia jutra". Książka łudząco podoba do wcześniejszej książki tego… Read More

9 miesięcy ago

Podsumowanie roku 2023

Cześć! Zapraszam na podsumowanie roku 2023. Książki Zacznijmy od książek. W tym roku cel 35 książek nie został osiągnięty. Niemniej… Read More

10 miesięcy ago

Podsumowanie: grudzień 2023

Zapraszam na krótkie podsumowanie miesiąca. Książki W grudniu skończyłem czytać Mein Kampf. Nudna książka. Ciekawsze fragmenty można by było streścić… Read More

11 miesięcy ago