2015-05-08 8 views
8

Kiedy map posiada wejścia różnej długości, wartość napełnienia None służy do brakujących wejść:Dlaczego mapa działa jak izip_longest z wypełnieniem = Brak?

>>> x = [[1,2,3,4],[5,6]] 
>>> map(lambda *x:x, *x) 
[(1, 5), (2, 6), (3, None), (4, None)] 

To samo zachowanie jak:

>>> import itertools 
>>> list(itertools.izip_longest(*x)) 
[(1, 5), (2, 6), (3, None), (4, None)] 

Co jest powodem map zapewnia to zachowanie , a nie następujące?

>>> map(lambda *x:x, *x) 
[(1, 5), (2, 6), (3,), (4,)] 

... i tam jest łatwym sposobem na uzyskanie tego ostatniego zachowania albo z jakiegoś smaku zip lub map?

+1

To jest dobrze [udokumentowane] (https://docs.python.org/2.7/library/functions.html#map) i został usunięty (lub powiedziany poprawiony) w Pythonie 3, aby uczynić go odpowiednikiem 'itertools.imap'. Przyczyna może znajdować się gdzieś w starym kodzie źródłowym lub liście adresowej CPython. –

+4

Rozważ: jaki powinien być wynik na [[1,2,3,4], [5,6], [7, 8, 9, 10]] "i czy będzie on różnił się od wyniku na' [[ 1,2,3,4], [5,6,7,8], [9,10]] "? – nneonneo

+1

* Z jakiego powodu mapa zapewnia takie zachowanie, a nie następujące? * Jeśli funkcja przyjmuje dwa wymagane argumenty pozycyjne, otrzymasz błąd, jeśli przekażesz tylko jeden. Tak więc, IMO, ma sens, aby Nic nie zastąpiło brakującego argumentu. Dodatkowo sprawdź komentarz @ nneonneo. Wypełnienie Brak jest jedynym sposobem, aby upewnić się, że elementy jednej i tylko jednej pozycji iteracyjnej są odwzorowane na każdy argument pozycyjny. – Shashank

Odpowiedz

5

Myślę, że ta decyzja projekt, który deweloperów rdzeniowe zdecydowaliśmy się w momencie zaimplementowano map. Nie ma powszechnie definiuje zachowanie dla map gdy jest używany z wieloma iterables, cytując z Map (higher-order function):

Mapa z 2 lub więcej list napotyka kwestię manipulacji gdy wykazy mają różne długości. Różne języki różnią się w tym; niektóre podnoszą wyjątek, niektóre zatrzymują się po długości najkrótszej listy i ignorują dodatkowe elementy na innych listach; niektóre kontynuują do długości najdłuższej listy, a dla list, które już się zakończyły, przekazują jakąś wartość zastępczą do funkcji bez wartości.

Tak, Python rdzeniowe deweloperów zdecydowała się na None jako zastępczy dla krótszych iterables w czasie map was introduced do Pythona w 1993

Jednak w przypadku itertools.imap nim zwarcie z najkrótszym iterable ponieważ jego konstrukcja jest mocno inspirowane językami takimi jak Standard ML, Haskell i APL. W Standard ML i Haskell map kończy się najkrótszą iteracją (nie jestem jednak pewien co do APL).

Python 3 usunięto również map(None, ...) (czy mówimy itertools.imap, Python 3 na map jest faktycznie prawie itertools.imap: Move map() from itertools to builtins) skonstruować ponieważ był obecny w Pythonie 2 tylko dlatego, że w czasie map dodano do Pythona nie było zip() funkcja w języku Python.

Od Issue2186: map and filter shouldn't support None as first argument (in Py3k only):

Zgadzam się z Guido, że nigdy nie stworzyli map(None, ...) jeśli zip() istniał. Podstawowy przypadek użycia jest przestarzały.


Aby uzyskać wynik, który chcesz Sugerowałbym użyciu itertools.izip_longest o wartości wartowniczego (object()) zamiast domyślnie None, None złamie rzeczy jeśli sam iterables zawierać None:

from itertools import izip_longest 


def solve(seq): 
    sentinel = object() 
    return [tuple(x for x in item if x is not sentinel) for item in 
      izip_longest(*seq, fillvalue=sentinel)] 


print solve([[1,2,3,4],[5,6]]) 
# [(1, 5), (2, 6), (3,), (4,)] 
+0

Brzmi dobrze. Wezmę to, że mówisz, że nie ma łatwego sposobu na usunięcie skróconego zachowania z jakiegoś smaku 'zip' lub' map' ... –

+2

O, znalazłeś bardziej odpowiednie referencje +1 :) Porzuciłem po jakimś czasie i [ zapytał Tim] (http://stackoverflow.com/questions/20320585/python-what-are-lambdas-for/20320604#comment48368640_20320604) gdyby mógł pomóc tutaj: D – thefourtheye

+0

'sentinel = object()': good suggestion, thanks . –

1

Biorąc pod uwagę, że pierwsza lista jest zawsze dłuższa i że istnieją tylko dwie listy, by zrobić coś takiego:

x = [1,2,3,4,5] 
y = ['a','b'] 
zip(x,y) + [(i,) for i in x[len(y):]] 
[(1, 'a'), (2, 'b'), (3,), (4,), (5,)] 
+0

Yup, a ostatnim krokiem byłoby użycie 'itertools.starmap':' list (starmap (lambda * x: x, that_list)) ' – Shashank

+0

To dodaje listę do' zip', więc nie. Poza tym możesz to zrobić: 'map (lambda * x: tuple (i dla i na x jeśli i nie jest None), * x)'. Więc tego nie szukałem. –