【问题标题】:pythonic way to do something N times without an index variable?pythonic方式在没有索引变量的情况下做N次?
【发布时间】:2011-02-27 14:03:06
【问题描述】:

每天我都越来越爱 python。

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

for i in xrange(N):
    do_something()

我不得不做 N 次。但每次都不取决于i(索引变量)的值。 我意识到我正在创建一个我从未使用过的变量 (i),我想“肯定有一种更 Python 的方式可以做到这一点,而不需要那个无用的索引变量。”

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

【问题讨论】:

  • 我刚刚了解了 _ 变量,否则我会考虑您使用 Pythonic 的方式。我认为我从未见过以任何其他方式完成的简单 for 循环,至少在 python 中是这样。虽然我确信在某些特定的用例中你会说“等等,这看起来很糟糕” - 但总的来说,xrange 是首选方式(据我所知)。
  • 注意:xrange 在 Python3 中不存在。请改用range

标签: coding-style for-loop python


【解决方案1】:

比在xrange(N) 上循环稍快的方法是:

import itertools

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

【讨论】:

  • 快多少? Python 3.1 还有区别吗?
  • @Hamish:我用 2.6 进行的测试说快了 32%(N=1000 时为 23.2 us vs 17.6 us)。但无论如何,那是一个真正的时间。我会默认使用 OP 的代码,因为它(对我来说)更易读。
  • 很高兴知道速度。我当然赞同 Mike 关于 OP 代码更具可读性的观点。
  • 你确定速度真的很重要吗?是不是这样,如果您在该循环中做任何重要的事情,它所花费的时间很可能与您选择的迭代样式一样多吗?
  • 除此之外,是的,它使用了一种名为“repeat”的方法来重复某些内容,因此更加清晰。但它也是一个更长更稀有的表达方式,并且还增加了额外的导入——这也消耗了时间和资源。我想这实际上是一个非常哲学的决定——基本上是我登陆这里的原因,在写完之后寻找上述的替代方案是在一段代码中。也就是说,我不是 100% 相信现在就去更改代码......
【解决方案2】:

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

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

【讨论】:

  • 不是反对者,但可能是因为您指的是另一篇文章,而不是在答案中添加更多细节
  • @Downgoat:感谢您的反馈。话虽如此,关于这个成语没有太多可说的。我引用另一篇文章的目的是指出搜索可能已经产生了答案。我觉得讽刺的是,这个问题的投票数是另一个问题的好几倍。
【解决方案3】:

我只使用for _ in range(n),直截了当。它会在 Python 2 中为大量数字生成整个列表,但如果您使用的是 Python 3,这不是问题。

【讨论】:

    【解决方案4】:

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

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

    然后你可以将函数作为参数传递。

    【讨论】:

    • @Hamish:几乎没有。 (在与 Alex 回答的时间相同的条件下,每个循环 17.8 us,相差 0.2 us)。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      我发现各种答案非常优雅(尤其是 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 比标准 for 循环提高了大约 30%,比 Martelli 的提高了 19%。

      【讨论】:

        【解决方案7】:

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

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

        【讨论】:

        • 当然。我们不仅要调用该函数一百万次,还要分配一百万个项目的列表。如果 CPU 工作正常,内存不应该也有点压力吗?答案不能被定性为绝对“没用”(它显示了一种不同的、有效的方法),所以我不能投反对票,但我不同意,我完全反对。
        • 不就是一个对同一个函数值的N个引用的列表吗?
        • 最好做fn() for fn in itertools.repeat(do_something, N) 并保存预生成数组...这是我的首选习惯用法。
        • @tzot 为什么是居高临下的语气?这个人努力写了一个答案,现在可能不鼓励将来做出贡献。即使它具有性能影响,它也是一个可行的选择,特别是如果 N 很小,性能/内存影响并不显着。
        • 我总是对 Python 开发人员对性能的痴迷感到惊讶 :) 虽然我同意这不是惯用的,而且 Python 新手阅读它可能不会像简单地理解发生了什么一样清楚使用迭代器
        【解决方案8】:

        一个简单的while循环怎么样?

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

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

        【讨论】:

        • 我唯一的想法是它是 3 行代码而不是 1 行 (?)
        • @AJP - 更像是 4 行 vs 2 行
        • 将比较(times > 0)和递减量(times -= 1)添加到开销中......比for循环慢......
        • @F1Rumors 还没有测量过,但是如果像 PyPy 这样的 JIT 编译器应该为这样一个简单的 while 循环生成更慢的代码,我会感到惊讶。
        猜你喜欢
        • 2019-12-15
        • 1970-01-01
        • 1970-01-01
        • 2019-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多