什么时候可以使用“星图"而不是“列表理解"? [英] When`starmap` could be preferred over `List Comprehension`
问题描述
在回答问题时笨拙计算递增的一组数字之间的差异,是否有更漂亮的方法?,我提出了两种解决方案,一种使用List Comprehension
,另一种使用
While answering the question Clunky calculation of differences between an incrementing set of numbers, is there a more beautiful way?, I came up with two solutions, one with List Comprehension
and other using itertools.starmap.
对我来说,list comprehension
语法看起来更清晰,易读,更冗长,并且更具Python风格.但是由于 starmap 在itertools中很好用,我当时想知道,一定有原因.
To me, list comprehension
Syntax looks more lucid, readable, less verbose and more Pythonic. But still as starmap is well available in itertools, I was wondering, there has to be a reason for it.
我的问题是,何时starmap
比List Comprehension
更可取?
My Question is whenstarmap
could be preferred over List Comprehension
?
注意如果是样式问题,则肯定与There should be one-- and preferably only one --obvious way to do it.
Note If its a matter of Style then it definitely contradicts There should be one-- and preferably only one --obvious way to do it.
头对头比较
可读性计数.--LC
这又是一个感知问题,但是对我来说LC
比starmap
更具可读性.
要使用starmap
,您需要导入operator
或定义lambda
或某些显式的multi-variable
函数,但是仍然需要从itertools
导入.
Its again a matter of perception but to me LC
is more readable than starmap
.
To use starmap
, either you need to import operator
, or define lambda
or some explicit multi-variable
function and nevertheless extra import from itertools
.
性能 --- LC
>>> def using_star_map(nums):
delta=starmap(sub,izip(nums[1:],nums))
return sum(delta)/float(len(nums)-1)
>>> def using_LC(nums):
delta=(x-y for x,y in izip(nums[1:],nums))
return sum(delta)/float(len(nums)-1)
>>> nums=[random.randint(1,10) for _ in range(100000)]
>>> t1=Timer(stmt='using_star_map(nums)',setup='from __main__ import nums,using_star_map;from itertools import starmap,izip')
>>> t2=Timer(stmt='using_LC(nums)',setup='from __main__ import nums,using_LC;from itertools import izip')
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=1000)/100000)
235.03 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=1000)/100000)
181.87 usec/pass
推荐答案
我通常看到的区别是map()
/starmap()
最适合您实际上只是在列表中的每个项目上调用函数的地方.在这种情况下,它们会更清晰一些:
The difference I normally see is map()
/starmap()
are most appropriate where you are literally just calling a function on every item in a list. In this case, they are a little clearer:
(f(x) for x in y)
map(f, y) # itertools.imap(f, y) in 2.x
(f(*x) for x in y)
starmap(f, y)
一旦开始需要同时放入lambda
或filter
,则应切换到列表comp/generator表达式,但是如果是单个函数,则语法对于列表理解的生成器表达式.
As soon as you start needing to throw in lambda
or filter
as well, you should switch up to the list comp/generator expression, but in cases where it's a single function, the syntax feels very verbose for a generator expression of list comprehension.
它们 是可互换的,并且在有疑问的情况下,请坚持使用生成器表达式,因为它通常更具可读性,但在简单情况下(map(int, strings)
,starmap(Vector, points)
)使用map()
/starmap()
有时可以使事情更容易阅读.
They are interchangeable, and where in doubt, stick to the generator expression as it's more readable in general, but in a simple case (map(int, strings)
, starmap(Vector, points)
) using map()
/starmap()
can sometimes make things easier to read.
我认为starmap()
更具可读性的示例:
An example where I think starmap()
is more readable:
from collections import namedtuple
from itertools import starmap
points = [(10, 20), (20, 10), (0, 0), (20, 20)]
Vector = namedtuple("Vector", ["x", "y"])
for vector in (Vector(*point) for point in points):
...
for vector in starmap(Vector, points):
...
对于map()
:
values = ["10", "20", "0"]
for number in (int(x) for x in values):
...
for number in map(int, values):
...
性能:
python -m timeit -s "from itertools import starmap" -s "from operator import sub" -s "numbers = zip(range(100000), range(100000))" "sum(starmap(sub, numbers))"
1000000 loops, best of 3: 0.258 usec per loop
python -m timeit -s "numbers = zip(range(100000), range(100000))" "sum(x-y for x, y in numbers)"
1000000 loops, best of 3: 0.446 usec per loop
用于构造namedtuple
:
python -m timeit -s "from itertools import starmap" -s "from collections import namedtuple" -s "numbers = zip(range(100000), reversed(range(100000)))" -s "Vector = namedtuple('Vector', ['x', 'y'])" "list(starmap(Vector, numbers))"
1000000 loops, best of 3: 0.98 usec per loop
python -m timeit -s "from collections import namedtuple" -s "numbers = zip(range(100000), reversed(range(100000)))" -s "Vector = namedtuple('Vector', ['x', 'y'])" "[Vector(*pos) for pos in numbers]"
1000000 loops, best of 3: 0.375 usec per loop
在我的测试中,我们正在谈论使用简单函数(没有lambda
),starmap()
比等效的生成器表达式要快.自然,除非是公认的瓶颈,否则性能应该在可读性上倒退.
In my tests, where we are talking about using simple functions (no lambda
), starmap()
is faster than the equivalent generator expression. Naturally, performance should take a back-seat to readability unless it's a proven bottleneck.
lambda
如何杀死任何性能提升的示例,与第一组示例相同,但使用lambda
而不是operator.sub()
:
Example of how lambda
kills any performance gain, same example as in the first set, but with lambda
instead of operator.sub()
:
python -m timeit -s "from itertools import starmap" -s "numbers = zip(range(100000), range(100000))" "sum(starmap(lambda x, y: x-y, numbers))"
1000000 loops, best of 3: 0.546 usec per loop
这篇关于什么时候可以使用“星图"而不是“列表理解"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!