列表理解与地图

时间:2009-08-07 23:43:32

标签: python list-comprehension map-function

是否有理由更喜欢使用map()而不是列表理解,反之亦然?它们中的任何一个通常比另一个更有效或者通常被认为是pythonic吗?

12 个答案:

答案 0 :(得分:587)

在某些情况下,

map在显微镜下可能更快(当你没有为此目的制作lambda,但在map和listcomp中使用相同的函数时)。在其他情况下,列表推导可能会更快,而且大多数(并非所有)pythonist认为它们更直接,更清晰。

使用完全相同的功能时,地图的微小速度优势的一个例子:

$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop

当map需要lambda时,如何完全颠倒性能比较的示例:

$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop

答案 1 :(得分:415)

答案 2 :(得分:89)

您应该使用mapfilter代替列表推导。

客观理由为什么你应该更喜欢它们,即使它们不是“Pythonic”是这样的:
它们需要函数/ lambdas作为参数,引入新范围

我不止一次被这个咬过:

for x, y in somePoints:
    # (several lines of code here)
    squared = [x ** 2 for x in numbers]
    # Oops, x was silently overwritten!

但如果相反我说:

for x, y in somePoints:
    # (several lines of code here)
    squared = map(lambda x: x ** 2, numbers)

然后一切都会好起来的。

你可以说我在同一范围内使用相同的变量名是愚蠢的。

我不是。代码原本很好 - 两个x不在同一范围内 只有在我移动内部块到问题出现的代码的不同部分之后(读取:维护期间的问题,而不是开发),我没想到它。

是的,如果你从未犯过这个错误那么列表理解就更优雅了 但是从个人经验(以及看到其他人犯了同样的错误)我已经看到它发生了足够多次,我认为当这些错误蔓延到你的代码中时,你不得不经历痛苦。

结论:

使用mapfilter。它们可以防止与范围相关的细微错误诊断。

旁注:

如果适合您的情况,请不要忘记考虑使用imapifilteritertools)!

答案 3 :(得分:39)

实际上,{3}语言中map和列表推导的行为完全不同。看看下面的Python 3程序:

def square(x):
    return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))

您可能希望它两次打印“[1,4,9]”行,而是打印“[1,4,9]”后跟“[]”。第一次看squares时,它似乎表现为三个元素的序列,但第二次表示为空元素。

在Python 2语言中,map返回一个普通的旧列表,就像列表推导在两种语言中一样。关键在于Python 3中的map(以及Python 2中的imap)的返回值不是列表 - 它是一个迭代器!

迭代迭代器时会消耗元素,而不是迭代列表。这就是squares在最后print(list(squares))行看起来为空的原因。

总结:

  • 在处理迭代器时,你必须记住它们是有状态的,并且当你遍历它们时它们会发生变异。
  • 列表更具可预测性,因为它们只有在您明确改变它们时才会改变;它们是容器
  • 还有一个好处:数字,字符串和元组更加可预测,因为它们根本无法改变;它们是

答案 4 :(得分:16)

我发现列表推导通常比map更能表达我所做的事情 - 它们都完成了它,但前者节省了试图理解什么可能是复杂{{1}的心理负担。 1}}表达。

在那里也有一个采访(我无法随意找到)Guido列出lambda和功能函数作为他最接近Python接受的事情,所以你可以提出这样的论点:因此,他们不是Pythonic。

答案 5 :(得分:15)

以下是一个可能的案例:

map(lambda op1,op2: op1*op2, list1, list2)

[op1*op2 for op1,op2 in zip(list1,list2)]

我猜如果你坚持使用列表推导而不是地图,你需要沉迷于zip()是一个不幸和不必要的开销。如果有人肯定地或肯定地澄清这一点,那就太好了。

答案 6 :(得分:15)

如果您计划编写任何异步,并行或分布式代码,您可能更喜欢map而不是列表理解 - 因为大多数异步,并行或分布式软件包都提供map函数过载python的map。然后通过将适当的map函数传递给代码的其余部分,您可能不必修改原始序列代码以使其并行运行(等)。

答案 7 :(得分:6)

因此,自Python 3起,map()是一个迭代器,你需要记住你需要什么:迭代器或list对象。

由于@AlexMartelli已经mentionedmap()只有在您不使用lambda函数时才会比列表理解更快。

我会给你一些时间比较。

<子> Python 3.5.2和CPython
我使用了Jupiter notebook,特别是%timeit内置的魔术命令
测量:s == 1000 ms == 1000 *1000μs= 1000 * 1000 * 1000 ns

设定:

x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))

内置功能:

%timeit map(sum, x_list)  # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop

%timeit list(map(sum, x_list))  # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop

%timeit [sum(x) for x in x_list]  # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop

lambda功能:

%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop

%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop

%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop

还有生成器表达式,请参阅PEP-0289。所以我认为将它添加到比较

是有用的
%timeit (sum(i) for i in x_list)
# Output: The slowest run took 6.66 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 495 ns per loop

%timeit list((sum(x) for x in x_list))
# Output: 1000 loops, best of 3: 319 µs per loop

%timeit (i+1 for i in i_list)
# Output: The slowest run took 6.83 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 506 ns per loop

%timeit list((i+1 for i in i_list))
# Output: 10000 loops, best of 3: 125 µs per loop

您需要list对象:

如果是自定义函数则使用列表推导,如果有内置函数则使用list(map())

您不需要list个对象,只需要可迭代的对象:

始终使用map()

答案 8 :(得分:1)

enter image description here

图片来源:Experfy

你可以自己看看哪个更好 - List Comprehension 和 Map Function

(与 map 函数相比,List Comprehension 处理 100 万条记录所需的时间更少)

希望有帮助!祝你好运:)

答案 9 :(得分:0)

我认为最Pythonic的方法是使用列表理解而不是mapfilter。原因是列表推导比mapfilter更清晰。

In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension

In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter

In [3]: odd_cubes == odd_cubes_alt
Out[3]: True

如您所见,理解不需要额外的lambda表达式作为map的需要。此外,理解还允许轻松过滤,而map需要filter才能过滤。

答案 10 :(得分:0)

我进行了一项快速测试,比较了三种调用对象方法的方法。在这种情况下,时差可以忽略不计,并且与所讨论的功能有关(请参阅@Alex Martelli的response)。在这里,我查看了以下方法:

# map_lambda
list(map(lambda x: x.add(), vals))

# map_operator
from operator import methodcaller
list(map(methodcaller("add"), vals))

# map_comprehension
[x.add() for x in vals]

我查看了整数(Python vals)和浮点数(Python int)的列表(存储在变量float中),以增加列表大小。考虑以下伪类DummyNum

class DummyNum(object):
    """Dummy class"""
    __slots__ = 'n',

    def __init__(self, n):
        self.n = n

    def add(self):
        self.n += 5

具体地说,是add方法。 __slots__属性是Python中的一种简单优化,它定义了类(属性)所需的总内存,从而减小了内存大小。 这是结果图。

Performance of mapping Python object methods

如前所述,所使用的技术所造成的影响很小,因此您应该以对您最易读的方式或在特定情况下进行编码。在这种情况下,列表推导(map_comprehension技术)对于对象中两种类型的加法最快,尤其是对于较短的列表。

访问this pastebin,查看用于生成绘图和数据的来源。

答案 11 :(得分:-1)

我尝试了@ alex-martelli的代码,但发现了一些差异

python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop

map即使在很大的范围内也要花费相同的时间,而使用列表理解则要花费很多时间,这从我的代码中可以明显看出。因此,除了被视为“ unpythonic”之外,我还没有遇到任何与使用地图有关的性能问题。