20

在 Python 2 中,一个常见的(旧的、遗留的)习惯用法是map使用如下形式连接长度不均匀的迭代器map(None,iter,iter,...)

>>> map(None,xrange(5),xrange(10,12))
[(0, 10), (1, 11), (2, None), (3, None), (4, None)]

在 Python 2 中,它被扩展,使得最长的迭代器是返回列表的长度,如果一个比另一个短,它用None.

在 Python 3 中,这是不同的。首先,您不能None用作位置 1 的可调用对象的参数:

>>> list(map(None, range(5),range(10,12)))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

好的——我可以这样解决:

>>> def f(*x): return x    
... 
>>> list(map(f, *(range(5),range(10,12))))
[(0, 10), (1, 11)]

但是现在,我有一个不同的问题:map返回最短迭代器的长度——不再用None.

当我将 Python 2 代码移植到 Python 3 时,这并不是一个可怕的罕见习语,而且我还没有找到一个简单的解决方案。

不幸的是,2to3 工具没有选择这一点——无益地建议:

-map(None,xrange(5),xrange(10,18))
+list(map(None,list(range(5)),list(range(10,18)))) 

建议?


编辑

有一些关于这个成语有多普遍的讨论。请参阅此 SO 帖子

我正在更新我还在上高中时编写的遗留代码。查看 Raymond Hettinger 正在编写和讨论的 2003 Python 教程,其中指出了 map 的这种特定行为......

4

4 回答 4

19

itertools.zip_longest做你想做的事,用一个更容易理解的名字。:)

于 2012-08-18T03:17:58.270 回答
2

这次我会回答我自己的问题。

使用 Python 3x,您可以像这样使用itertools.zip_longest

>>> list(map(lambda *a: a,*zip(*itertools.zip_longest(range(5),range(10,17)))))
[(0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (None, 15), (None, 16)]

我想你也可以自己动手:

>>> def oldMapNone(*ells):
...     '''replace for map(None, ....), invalid in 3.0 :-( '''
...     lgst = max([len(e) for e in ells])
...     return list(zip(* [list(e) + [None] * (lgst - len(e)) for e in ells]))
... 
>>> oldMapNone(range(5),range(10,12),range(30,38))
[(0, 10, 30), (1, 11, 31), (2, None, 32), (3, None, 33), (4, None, 34), (None, None, 35), (None, None, 36), (None, None, 37)]
于 2012-08-18T07:01:54.920 回答
1

如果您需要 Python 2 中的一些过时功能,则一种方法是另一种方法 - 自己编写。当然,它不是内置功能,但至少它是一些东西。

下面的代码片段需要 27 行

#!/usr/bin/env python3

def fetch(sequence, index):
  return None if len(sequence) <= index else sequence[index]

def mymap(f, *args):
  max_len = 0
  for i in range(len(args)): 
      max_len = max(max_len, len(args[i]))
  out = []
  for i in range(max_len):
      t = []
      for j in range(len(args)): 
          t.append(fetch(args[j],i))      

      if f != None:
          # Use * for unpack arguments from Arbitarily argument list
          # Use ** for unpack arguments from Keyword argument list
          out.append(f(*t))
      else:
          out.append(tuple(t))
  return out 

if __name__ == '__main__':
    print(mymap(None, [1,2,3,4,5],[2,1,3,4],[3,4]))
    print(mymap(None,range(5),range(10,12)))
于 2020-05-12T20:30:51.603 回答
-1

你可以像这样解决问题: list(map(lambda x, y: (x, y),[1, 2, 3 ,4, 5], [6, 7, 8, 9, 10]))

于 2017-11-28T07:33:27.317 回答