在回答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
还是一个感知问题,但是对我来说LCstarmap更具可读性。
要使用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)
一旦开始需要同时输入lambdafilter,就应该切换到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/

10-10 20:55