Python 3与Python 2映射行为

时间:2012-08-18 03:13:10

标签: python python-3.x map-function

在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工具does not选择了这个 - 无益建议:

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

建议?


修改

有一些讨论这个成语有多常见。 See this SO post

当我还在高中时,我正在更新编写的遗留代码。看看Raymond Hettinger撰写的2003 Python教程written and discussed,指出了这个特定的地图行为。

3 个答案:

答案 0 :(得分:18)

itertools.zip_longest以更易于理解的名义做你想做的事。 :)

答案 1 :(得分: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)]

答案 2 :(得分:0)

如果需要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)))