programowanieInżynier Danych

Opisz działanie funkcji map() w Pythonie. Czym różni się od wyrażeń generatorowych i wyrażeń listowych, w jakich przypadkach map jest preferowany, a kiedy nie?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

Funkcja map() pochodzi z programowania funkcyjnego (jeszcze z Lispa), jej zadaniem jest stosowanie funkcji do każdego elementu sekwencji, zwracając nowy obiekt iterowalny z wynikami. W Pythonie powstała na długo przed pojawieniem się wyrażeń generatorowych i wyrażeń listowych, ale wciąż jest szeroko stosowana, szczególnie w dużych pipelines przetwarzania danych.

Problem

Jeśli konieczne jest przekształcenie każdego elementu kolekcji według jakiejś zasady, rozwiązania typu for-pętli szybko stają się nieczytelne. Wyrażenia listowe i map() pozwalają wyrazić zamiar w bardziej zwięzły sposób. Ważne jest, aby zrozumieć różnice między tymi podejściami, aby prawidłowo wybrać narzędzie.

Rozwiązanie

  • map(func, iterable) tworzy leniwego iteratora, w którym elementy oryginalnej sekwencji są przekształcane przez funkcję func
  • W Pythonie 3 obiekt map nie oblicza elementów od razu, a działa leniwie
def square(x): return x * x squares = map(square, [1, 2, 3]) print(list(squares)) # [1, 4, 9]

Wyrażenie listowe [square(x) for x in [1, 2, 3]] robi to samo, ale od razu zwraca listę, a wyrażenie generatorowe (square(x) for x in [1, 2, 3]) to leniwy generator.

Kluczowe cechy:

  • map() zawsze zwraca leniwego iteratora
  • Wygodne do pracy z wieloma kolekcjami (z wieloma argumentami) — map(f, a, b)
  • Nie obsługuje warunków filtracji w bezpośredniej składni (w przeciwieństwie do list comprehension)

Pytania z haczykiem.

Czy map() zwraca listę w Pythonie 3?

Nie, od momentu wprowadzenia Pythona 3, map() zwraca nie listę, a leniwego iteratora. Aby uzyskać listę — trzeba ręcznie owijać wynik w list().

res = map(str.upper, ['a', 'b']) print(res) # <map object ...> print(list(res)) # ['A', 'B']

Czy można za pomocą map() dodać warunek filtrowania wewnątrz funkcji?

Nie, map() nie filtruje elementów, a jedynie przekształca. Do filtrowania używaj filter() lub wyrażenia listowego:

result = map(str.upper, ['a', 'b', None]) # Jeśli przyjdzie None, map wywoła błąd wywołania str.upper(None) # filter pomoże usunąć None przed map

Czy można za pomocą map() jednocześnie iterować po dwóch sekwencjach?

Tak, map() potrafi przyjmować dowolną liczbę sekwencji, a funkcja-przekształcająca musi przyjmować tyle samo argumentów:

x = [1, 2, 3] y = [10, 20, 30] result = map(lambda a, b: a + b, x, y) print(list(result)) # [11, 22, 33]

Typowe błędy i antywzorce

  • Oczekiwanie, że map() zwraca listę w Pythonie 3
  • Próba filtrowania wewnątrz map() — map tylko przekształca
  • Przekazywanie sekwencji o różnej długości — linie są przycinane do minimalnej długości

Przykład z życia

Negatywny przypadek

Programista oczekuje, że otrzyma listę od razu:

mapped = map(abs, [-1, -2, -3]) print(mapped[1]) # TypeError: 'map' object is not subscriptable

Zalety:

  • Oszczędza pamięć

Wady:

  • Błąd przy próbie uzyskania dostępu po indeksie
  • Wymaga przekształcenia w list()

Pozytywny przypadek

Użycie wyrażenia generatorowego do filtrowania i map() do przekształcania:

nums = range(-5, 6) positives = (x for x in nums if x > 0) sq = map(lambda n: n * n, positives) print(list(sq)) # [1, 4, 9, 16, 25, 36]

Zalety:

  • Prosto łączyć z filtrami
  • Minimalne zużycie pamięci

Wady:

  • Mniejsza czytelność dla nowicjuszy
  • Brak wsparcia składniowego dla warunku