为什么 map 像 izip_longest 和 fill=None 一样工作?

Why does map work like izip_longest with fill=None?

map 有不同长度的输入时,None 的填充值用于缺少的输入:

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

这与以下行为相同:

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

map 提供此行为而不是以下行为的原因是什么?

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

…有没有一种简单的方法可以使后一种行为具有某种 zipmap 的味道?

鉴于第一个列表总是更长并且只有两个列表,您可以这样做:

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,)]

我认为这是核心开发人员在实施时选择的设计决策 map。当 map 与多个可迭代对象一起使用时,没有普遍定义的行为,引用自 Map (higher-order function):

Map with 2 or more lists encounters the issue of handling when the lists are of different lengths. Various languages differ on this; some raise an exception, some stop after the length of the shortest list and ignore extra items on the other lists; some continue on to the length of the longest list, and for the lists that have already ended, pass some placeholder value to the function indicating no value.

因此,Python 核心开发人员在 1993 年 map was introduced 到 Python 期间选择 None 作为较短迭代的占位符。

但在 itertools.imap 的情况下,它会与最短的可迭代对象短路,因为它的设计在很大程度上受到标准 ML、Haskell 和 APL 等语言的启发。在标准 ML 和 Haskell 中,map 以最短的可迭代结束(不过我不确定 APL)。

Python 3也去掉了map(None, ...)(或者应该说itertools.imap,Python 3的map其实差不多是itertools.imapMove map() from itertools to builtins) 构造,因为它出现在 Python 2 中只是因为当时 map 被添加到 Python Python 中没有 zip() 函数].

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

I concur with Guido that we never would have created map(None, ...) if zip() had existed. The primary use case is obsolete.


为了得到你想要的结果,我建议使用 itertools.izip_longest 和标记值 (object()) 而不是默认的 None,如果 None 会破坏事情迭代本身包含 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,)]