为什么 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,)]
…有没有一种简单的方法可以使后一种行为具有某种 zip
或 map
的味道?
鉴于第一个列表总是更长并且只有两个列表,您可以这样做:
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.imap
: Move 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,)]
当 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,)]
…有没有一种简单的方法可以使后一种行为具有某种 zip
或 map
的味道?
鉴于第一个列表总是更长并且只有两个列表,您可以这样做:
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.imap
: Move 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, ...)
ifzip()
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,)]