TL;DR map函数及其类似函数在python2和python3下表现差异很大,py2下返回list,而py3下返回迭代器。解决办法是使用list函数显式求值。

下面是关于map函数的代码示例,透过在python2和python3不同行为,为你展现不同:

导入相关模块

    import time

定义我们的task函数,通过打印输出让我们了解实际工作情况

    def task(x):
        print("round: {}, I am start to sleep".format(x))
        time.sleep(1)
        print("round: {}, I am finished sleep".format(x))
        return pow(x, 2)

将可迭代对象map到task函数

    map(task, range(4))

python2中的map函数

在python2中执行以上代码,可以得到如下输出:

        round: 0, I am start to sleep
        round: 0, I am finished sleep
        round: 1, I am start to sleep
        round: 1, I am finished sleep
        round: 2, I am start to sleep
        round: 2, I am finished sleep
        round: 3, I am start to sleep
        round: 3, I am finished sleep

        [0, 1, 4, 9]

python3中的map函数

在python3中执行以上代码,可以得到如下输出:

    <map at 0x7f9df0559b00>

很不幸,你的task代码并没有执行,无论是打印输出还是返回值,都没有执行。 那是因为python2中的map是Apply function to every item of iterable and return a list of the results.而python3中的map是Return an iterator that applies function to every item of iterable, yielding the results.

这个例子中,使用了交互式编程,用户可以直观的看到返回结果,但是在非交互式使用场景(比如作为模块运行)时,如果没有收集返回值也没有类似打印输出的情况下,一切看似正常,但实际没有运行的情况,将造成难以调试的bug。

解决不同

如何才能让python3的map函数的行为和python2的一样呢?

答案是使用list函数,将上述代码稍作改动:

list(map(task, range(4)))

那么你将得到输出:

        round: 0, I am start to sleep
        round: 0, I am finished sleep
        round: 1, I am start to sleep
        round: 1, I am finished sleep
        round: 2, I am start to sleep
        round: 2, I am finished sleep
        round: 3, I am start to sleep
        round: 3, I am finished sleep

        [0, 1, 4, 9]

这将和python2一模一样。

更大范围的不同

很不幸的是python2和python3的差异不仅仅在一个map函数上,很多函数也存在类似的差异,如multiprocessing模块的Pool类的imap_unorderedmap方法,实际上python2和python3差异还是比较大的,所以如果遇到兼容性问题,第一件事情就是立即查阅官方文档。