Tim*_*man 680 python list-comprehension map-function
是否有理由更喜欢使用map()超过列表理解,反之亦然?它们中的任何一个通常比另一个更有效或被认为是更加pythonic?
Ale*_*lli 625
map在某些情况下(当你没有为此目的制作lambda,但在map和listcomp中使用相同的函数时)可能在显微镜下更快.在其他情况下,列表推导可能更快,并且大多数(并非所有)pythonistas认为它们更直接和更清晰.
使用完全相同的功能时,地图的微小速度优势的一个例子:
$ 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
Run Code Online (Sandbox Code Playgroud)
当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
Run Code Online (Sandbox Code Playgroud)
nin*_*cko 441
案例
map,虽然它被认为是'unpythonic'.例如,比map(sum, myLists)它更优雅/简洁[sum(x) for x in myLists].你获得不必弥补虚拟变量(如的风采sum(x) for x...或sum(_) for _...或sum(readableName) for readableName...),你必须输入两次,只是进行迭代.同样的道理也适用于filter和reduce从什么itertools模块:如果你已经有一个方便的功能,你可以继续前进,做一些函数式编程.这在某些情况下会增加可读性,在其他情况下会丢失(例如,新手程序员,多个参数)......但是代码的可读性在很大程度上取决于您的评论.map功能作为一个纯抽象功能而做功能编程,在那里你映射map,或讨好map,或以其他方式谈论受益map的功能.例如,在Haskell中,一个函数接口称为fmap泛化任何数据结构的映射.这在python中非常罕见,因为python语法迫使你使用生成器风格来讨论迭代; 你不能轻易地概括它.(这有时很好,有时也很糟糕.)你可能会想出一些罕见的python例子,这map(f, *lists)是合理的事情.我能想出的最接近的例子是sumEach = partial(map,sum),这是一个非常大致相当于以下内容的单线程:def sumEach(myLists):
return [sum(_) for _ in myLists]
Run Code Online (Sandbox Code Playgroud)
for-loop:您当然也可以使用for循环.虽然从功能编程的角度来看并不那么优雅,但有时非局部变量会使命令式编程语言(如python)中的代码更加清晰,因为人们习惯于以这种方式阅读代码.当你只是进行任何复杂的操作时,for循环通常也是最有效的,这些操作没有构建像list-comprehensions这样的列表,并且map被优化(例如求和,或者做树等) - 至少在记忆方面有效(不一定是在时间方面,我认为在最坏的情况下是一个恒定的因素,除非一些罕见的病态垃圾收集打嗝)."Pythonism"
我不喜欢"pythonic"这个词,因为我没有发现pythonic在我眼中总是很优雅.尽管如此,map和filter类似的功能(如非常有用的itertools模块)在风格方面可能被认为是非语音的.
怠惰
就效率而言,就像大多数函数式编程结构一样,MAP可能是LaZY,而事实上它在python中是懒惰的.这意味着您可以执行此操作(在python3中),并且您的计算机不会耗尽内存并丢失所有未保存的数据:
>>> map(str, range(10**100))
<map object at 0x2201d50>
Run Code Online (Sandbox Code Playgroud)
尝试使用列表理解:
>>> [str(n) for n in range(10**100)]
# DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #
Run Code Online (Sandbox Code Playgroud)
请注意,列表推导也本质上是懒惰的,但是python选择将它们实现为非惰性.尽管如此,python确实以生成器表达式的形式支持惰性列表推导,如下所示:
>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>
Run Code Online (Sandbox Code Playgroud)
您基本上可以将[...]语法视为将生成器表达式传递给列表构造函数,例如list(x for x in range(5)).
简短的人为例子
from operator import neg
print({x:x**2 for x in map(neg,range(5))})
print({x:x**2 for x in [-y for y in range(5)]})
print({x:x**2 for x in (-y for y in range(5))})
Run Code Online (Sandbox Code Playgroud)
列表推导是非惰性的,因此可能需要更多内存(除非您使用生成器理解).方括号[...]通常会使事情变得明显,尤其是在括号中.另一方面,有时你最终会像打字一样啰嗦[x for x in....只要保持迭代器变量很短,如果不缩进代码,列表推导通常会更清晰.但是你总是可以缩进你的代码.
print(
{x:x**2 for x in (-y for y in range(5))}
)
Run Code Online (Sandbox Code Playgroud)
或打破局面:
rangeNeg5 = (-y for y in range(5))
print(
{x:x**2 for x in rangeNeg5}
)
Run Code Online (Sandbox Code Playgroud)
python3的效率比较
map 现在很懒:
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop ^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
因此,如果您不使用所有数据,或者不提前知道您需要多少数据,那么mappython3(以及python2或python3中的生成器表达式)将避免在必要的最后时刻之前计算它们的值.通常这通常会超过使用的任何开销map.缺点是python与大多数函数式语言相比非常有限:如果从"按顺序"从左到右访问数据,则只能获得此优势,因为python生成器表达式只能按顺序进行评估x[0], x[1], x[2], ....
但是,假设我们有一个f我们想要的预制函数map,我们忽略了map立即强制评估的懒惰list(...).我们得到一些非常有趣的结果:
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'
10000 loops, best of 3: 165/124/135 usec per loop ^^^^^^^^^^^^^^^
for list(<map object>)
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'
10000 loops, best of 3: 181/118/123 usec per loop ^^^^^^^^^^^^^^^^^^
for list(<generator>), probably optimized
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'
1000 loops, best of 3: 215/150/150 usec per loop ^^^^^^^^^^^^^^^^^^^^^^
for list(<generator>)
Run Code Online (Sandbox Code Playgroud)
结果是AAA/BBB/CCC形式,其中A是在大约2010年英特尔工作站上使用python 3执行的.?.?,B和C是使用带有python 3.2.1的大约2013 AMD工作站执行的,硬件极其不同.结果似乎是地图和列表推导在性能方面具有可比性,受其他随机因素的影响最大.我们可以告诉的唯一的事情似乎是,奇怪的是,虽然我们期待列表解析[...]比生成表情表现得更好(...),map也更高效,发电机表达式(再次假设计算所有的值/使用).
重要的是要意识到这些测试假设一个非常简单的功能(身份功能); 但这很好,因为如果功能很复杂,那么与程序中的其他因素相比,性能开销可以忽略不计.(用其他简单的东西测试可能仍然很有趣f=lambda x:x+x)
如果你擅长阅读python程序集,你可以使用该dis模块来查看幕后是否真的发生了什么:
>>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval')
>>> dis.dis(listComp)
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_NAME 0 (xs)
9 GET_ITER
10 CALL_FUNCTION 1
13 RETURN_VALUE
>>> listComp.co_consts
(<code object <listcomp> at 0x2511a48, file "listComp", line 1>,)
>>> dis.dis(listComp.co_consts[0])
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 18 (to 27)
9 STORE_FAST 1 (x)
12 LOAD_GLOBAL 0 (f)
15 LOAD_FAST 1 (x)
18 CALL_FUNCTION 1
21 LIST_APPEND 2
24 JUMP_ABSOLUTE 6
>> 27 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
>>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval')
>>> dis.dis(listComp2)
1 0 LOAD_NAME 0 (list)
3 LOAD_CONST 0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>)
6 MAKE_FUNCTION 0
9 LOAD_NAME 1 (xs)
12 GET_ITER
13 CALL_FUNCTION 1
16 CALL_FUNCTION 1
19 RETURN_VALUE
>>> listComp2.co_consts
(<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,)
>>> dis.dis(listComp2.co_consts[0])
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 17 (to 23)
6 STORE_FAST 1 (x)
9 LOAD_GLOBAL 0 (f)
12 LOAD_FAST 1 (x)
15 CALL_FUNCTION 1
18 YIELD_VALUE
19 POP_TOP
20 JUMP_ABSOLUTE 3
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
>>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval')
>>> dis.dis(evalledMap)
1 0 LOAD_NAME 0 (list)
3 LOAD_NAME 1 (map)
6 LOAD_NAME 2 (f)
9 LOAD_NAME 3 (xs)
12 CALL_FUNCTION 2
15 CALL_FUNCTION 1
18 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
似乎使用[...]语法比使用语法更好list(...).可悲的是,这个map类对于反汇编来说有点不透明,但我们可以通过速度测试来实现.
Meh*_*dad 90
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!
Run Code Online (Sandbox Code Playgroud)
但如果相反我说:
for x, y in somePoints:
# (several lines of code here)
squared = map(lambda x: x ** 2, numbers)
Run Code Online (Sandbox Code Playgroud)
那么一切都会好起来的.
你可以说我在同一范围内使用相同的变量名称是愚蠢的.
我不是.代码原本很好 - 两个x不在同一范围内.
只是在我将内部块移动到代码的不同部分后才出现问题(读取:维护期间出现问题,而不是开发),我没想到.
是的,如果你从未犯过这个错误,那么列表理解就更优雅了.
但是从个人经验(以及看到其他人犯同样的错误)我已经看到它发生了足够多次,我认为当这些错误蔓延到你的代码中时,你不得不经历这种痛苦.
使用map和filter.它们可以防止难以诊断的与范围相关的细微错误.
不要忘记考虑使用imap和ifilter(in itertools)它们是否适合您的情况!
rae*_*aek 41
实际上,map列表推导在Python 3语言中的表现完全不同.看看下面的Python 3程序:
def square(x):
return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))
Run Code Online (Sandbox Code Playgroud)
您可能希望它两次打印"[1,4,9]"行,而是打印"[1,4,9]"后跟"[]".你第一次看squares它似乎表现为三个元素的序列,但第二次作为空元素.
在Python 2语言中map返回一个简单的旧列表,就像列表推导在两种语言中一样.关键是mapPython 3(以及imapPython 2)中的返回值不是列表 - 它是一个迭代器!
迭代迭代器时会消耗元素,这与迭代列表时不同.这就是为什么squares在最后print(list(squares))一行看起来为空的原因.
总结一下:
Dan*_*Dan 16
我发现列表理解通常更能表达我想要做的事情map- 它们都完成了它,但前者节省了试图理解什么是复杂lambda表达的心理负担.
在那里也有一个采访(我无法随意找到)Guido列出lambdas和功能函数作为他最接近Python接受的事情,所以你可以通过美德来证明他们是非Pythonic那个.
Mik*_*rns 16
如果您计划编写任何异步,并行或分布式代码,您可能更喜欢map列表解析 - 因为大多数异步,并行或分布式包提供了一个map函数来重载python map.然后通过将适当的map函数传递给代码的其余部分,您可能不必修改原始的串行代码以使其并行运行(等).
And*_*ndz 15
这是一个可能的情况:
map(lambda op1,op2: op1*op2, list1, list2)
Run Code Online (Sandbox Code Playgroud)
与:
[op1*op2 for op1,op2 in zip(list1,list2)]
Run Code Online (Sandbox Code Playgroud)
我猜你的zip()是一个不幸和不必要的开销,如果你坚持使用列表推导而不是地图,你需要沉迷.如果有人澄清这一点是肯定的还是消极的,那将是很好的.
所以,由于Python 3 map()是一个迭代器,你需要记住你需要什么:迭代器或list对象.
正如@AlexMartelli已经提到的,map()只有在你不使用lambda函数时才比列表理解更快.
我会告诉你一些时间比较.
Python 3.5.2和CPython
我使用了Jupiter笔记本,特别是%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))
Run Code Online (Sandbox Code Playgroud)
内置功能:
%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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
还有生成器表达这样的东西,参见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
Run Code Online (Sandbox Code Playgroud)
list对象:如果是自定义函数,请使用list comprehension,list(map())如果有内置函数则使用
list对象,你只需要可迭代的对象:一直用map()!