在回答Clunky calculation of differences between an incrementing set of numbers, is there a more beautiful way?问题时,我提出了两种解决方案,一种是List Comprehension
,另一种是itertools.starmap。
对我而言,list comprehension
语法看起来更清晰,易读,更冗长,并且更具Python风格。但是,由于starmap在itertools中很容易获得,我想知道,这一定有原因。
我的问题是,何时可以使用starmap
而不是List Comprehension
?
注意如果它是样式问题,那么它肯定与There should be one-- and preferably only one --obvious way to do it.
相矛盾
头对头比较
可读性很重要。 -LC
还是一个感知问题,但是对我来说LC
比starmap
更具可读性。
要使用starmap
,您需要导入operator
,或定义lambda
或某些显式的multi-variable
函数,然后从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()
最合适,您实际上只是在列表中的每个项目上调用一个函数。在这种情况下,它们会更清晰一些:
(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
,就应该切换到list comp/generator表达式,但是在它是单个函数的情况下,对于list comprehension的生成器表达式来说,语法非常冗长。它们是可互换的,并且在一般情况下更具可读性,因此有疑问会坚持使用生成器表达式,但是在简单情况下(
map(int, strings)
,starmap(Vector, points)
),使用map()
/starmap()
有时会使事情更易于阅读。例子:
我认为
starmap()
更具可读性的示例: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()
比等效的生成器表达式要快。自然,除非是公认的瓶颈,否则性能应该在可读性上倒退。lambda
如何杀死任何性能提升的示例,与第一个示例相同,但是使用lambda
而不是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
关于python - 什么时候可以使用``starmap''而不是 `List Comprehension`,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10448486/