pythonic的方式做N次没有索引变量?


161

我每天都越来越喜欢python。

今天,我正在编写一些代码,例如:

for i in xrange(N):
    do_something()

我必须做N次。但是每次都不依赖于i(索引变量)的值。我意识到我正在创建一个我从未使用过的变量(i),并且我想:“无疑,这是一种更加Python化的方法,不需要那个无用的索引变量。”

所以...问题是:您知道如何以更(pythonic)漂亮的方式完成此简单任务吗?


7
我刚刚了解了_变量,但否则我会考虑使用Pythonic的方式。我认为我从未见过任何其他方式(至少在python中)完成过一个简单的for循环。尽管我确定在特定的用例中会看到并说“等等,这看起来很糟糕”-但总的来说,xrange是首选的方式(据我所知)。
韦恩·维尔纳


5
注意:xrange在Python3中不存在。使用range代替。
约翰·亨克尔

Answers:


110

比循环更快的方法xrange(N)是:

import itertools

for _ in itertools.repeat(None, N):
    do_something()

3
快多少?Python 3.1仍然有区别吗?
Hamish Grubijan

15
@Hamish:我的2.6测试表明速度提高了32%(对于N = 1000,测试速度为23.2 us vs 17.6 us)。但这毕竟是一个真正的时间。我会默认使用OP的代码,因为(对我而言)它更易于阅读。
Mike Boers 2010年

3
很高兴知道速度。我当然回应Mike关于OP的代码更具可读性的观点。
韦恩·维尔纳

@Wayne,我想这个习惯真的非常有用-除了您已经习惯了,为什么每次执行此计数时,“为什么从0计数到N-1 [[并完全忽略计数]] -独立操作”本质上比“重复N次以下操作”更清晰...?
Alex Martelli 2010年

4
您确定速度确实合适吗?是不是这样,如果您在该循环中做任何重要的事情,很可能会花费您选择的迭代样式几百或数千个时间?
亨宁2014年

55

使用_变量,正如我在问这个问题时所了解到的,例如:

# A long way to do integer exponentiation
num = 2
power = 3
product = 1
for _ in xrange(power):
    product *= num
print product

6
不是
下降投票者,

5
@Downgoat:感谢您的反馈。就是说,关于这个习语没有太多要说的。我提到另一篇文章的目的是指出搜索可能会找到答案。具有讽刺意味的是,这个问题的投票数是其他问题的数倍。
GreenMatt


10

由于函数是一等公民,因此您可以编写小型包装器(来自Alex的回答)

def repeat(f, N):
    for _ in itertools.repeat(None, N): f()

那么您可以将函数作为参数传递。


@Hamish:几乎没有。(在与Alex回答时间相同的条件下,每个循环17.8 us,相差0.2 us)。
Mike Boers 2010年

9

_与x相同。但是,这是一个python惯用法,用于指示您不打算使用的标识符。在python中,这些标识符不会像其他语言中的变量那样占用内存或分配空间。很容易忘记这一点。它们只是指向对象的名称,在这种情况下,每次迭代都是一个整数。


8

我发现各种答案确实很不错(尤其是Alex Martelli的答案),但是我想直接量化性能,因此我编写了以下脚本:

from itertools import repeat
N = 10000000

def payload(a):
    pass

def standard(N):
    for x in range(N):
        payload(None)

def underscore(N):
    for _ in range(N):
        payload(None)

def loopiter(N):
    for _ in repeat(None, N):
        payload(None)

def loopiter2(N):
    for _ in map(payload, repeat(None, N)):
        pass

if __name__ == '__main__':
    import timeit
    print("standard: ",timeit.timeit("standard({})".format(N),
        setup="from __main__ import standard", number=1))
    print("underscore: ",timeit.timeit("underscore({})".format(N),
        setup="from __main__ import underscore", number=1))
    print("loopiter: ",timeit.timeit("loopiter({})".format(N),
        setup="from __main__ import loopiter", number=1))
    print("loopiter2: ",timeit.timeit("loopiter2({})".format(N),
        setup="from __main__ import loopiter2", number=1))

我还提出了另一种解决方案,该解决方案基于Martelli的解决方案,并用于map()调用有效负载函数。好的,我有点作弊,因为我可以自由地使有效负载接受被丢弃的参数:我不知道是否有解决方法。不过,结果如下:

standard:  0.8398549720004667
underscore:  0.8413165839992871
loopiter:  0.7110594899968419
loopiter2:  0.5891903560004721

因此使用map会比loop的标准提高约30%,比Martelli的标准提高19%。


4

假设您已将do_something定义为一个函数,并且您想执行N次。也许您可以尝试以下方法:

todos = [do_something] * N  
for doit in todos:  
    doit()

45
当然。我们不仅要调用该函数一百万次,还要分配一百万个项目的列表。如果CPU工作正常,内存是否也不会有点压力?答案不能被描述为绝对“无用”(它显示了一种不同的,有效的方法),所以我不能拒绝投票,但是我不同意,我完全反对。
tzot 2010年

1
不仅仅是一个引用了相同函数值的N个引用的列表吗?
Nick McCurdy

更好地做fn() for fn in itertools.repeat(do_something, N)并保存预先生成的数组...这是我惯用的习惯用法。
F1Rumors

1
@tzot为什么居高临下的语气?此人努力写出答案,现在可能不愿在将来做出贡献。即使对性能有影响,这也是一个可行的选择,尤其是当N较小时,对性能/内存的影响并不重要。
davidscolgan

我一直对Python开发人员痴迷于性能感到惊讶:)尽管我同意它不是惯用的,并且
刚接触

1

一个简单的while循环呢?

while times > 0:
    do_something()
    times -= 1

您已经有了变量;为什么不使用它?


1
我唯一的想法是,这是3行代码与一(?)
AJP

2
@AJP-更像是4行vs 2行
ArtOfWarfare 2013年

将比较(时间> 0)和减量(时间= 1)添加到开销中……如此慢于for循环...
F1Rumors

@ F1Rumors还没有测量它,但是如果像PyPy这样的JIT编译器为这样一个简单的while循环生成较慢的代码,我会感到惊讶。
PhilippClaßen18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.