是否有理由更喜欢使用map()
而不是列表理解,反之亦然?它们中的任何一个通常比另一个更有效或者通常被认为是pythonic吗?
答案 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)
map
和filter
代替列表推导。 客观理由为什么你应该更喜欢它们,即使它们不是“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
不在同一范围内
只有在我移动内部块到问题出现的代码的不同部分之后(读取:维护期间的问题,而不是开发),我没想到它。
是的,如果你从未犯过这个错误那么列表理解就更优雅了 但是从个人经验(以及看到其他人犯了同样的错误)我已经看到它发生了足够多次,我认为当这些错误蔓延到你的代码中时,你不得不经历痛苦。
使用map
和filter
。它们可以防止与范围相关的细微错误诊断。
如果适合您的情况,请不要忘记考虑使用imap
和ifilter
(itertools
)!
答案 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已经mentioned,map()
只有在您不使用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)
图片来源:Experfy
你可以自己看看哪个更好 - List Comprehension 和 Map Function
(与 map 函数相比,List Comprehension 处理 100 万条记录所需的时间更少)
希望有帮助!祝你好运:)
答案 9 :(得分:0)
我认为最Pythonic的方法是使用列表理解而不是map
和filter
。原因是列表推导比map
和filter
更清晰。
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中的一种简单优化,它定义了类(属性)所需的总内存,从而减小了内存大小。
这是结果图。
如前所述,所使用的技术所造成的影响很小,因此您应该以对您最易读的方式或在特定情况下进行编码。在这种情况下,列表推导(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”之外,我还没有遇到任何与使用地图有关的性能问题。