有没有可能创建一个没有迭代变量的`for`循环?(如何使代码循环指定的次数?)

236
可以不用标签来实现以下功能吗?
for i in range(some_number):
    # do something

如果你只是想要做某件事情N次,而不需要迭代器。

24
这是一个好问题!PyDev甚至将'i'标记为“未使用的变量”的警告。以下解决方案可消除此警告。 - Ashwin Nanjappa
1
@Ashwin 你可以使用 @UnusedVariable 来消除那个警告。请注意,我需要转义 'at' 符号才能通过这个注释。 - khatchad
我听到了和你一样的问题。使用pylint警告非常恼人。当然,你可以通过额外的抑制方式(如@Raffi Khatchadourian建议的)来禁用警告。避免pylint警告和抑制注释将是很好的。 - tangoal
16个回答

140

就我所知,没有。

我认为你最好做的是这样:

def loop(f,n):
    for i in xrange(n): f()

loop(lambda: <insert expression here>, 5)

但我认为你可以接受多余的 i 变量。

这里有一个使用 _ 变量的选项,实际上,它只是另一个变量。

for _ in range(n):
    do_something()

请注意,_在Python交互式会话中被赋值为最后一个返回的结果:

>>> 1+2
3
>>> _
3

因此,我不会以这种方式使用它。我不知道Ryan提到的任何习语。它可能会搞砸你的解释器。

>>> for _ in xrange(10): pass
...
>>> _
9
>>> 1+2
3
>>> _
9

根据Python语法,这是一个可以接受的变量名:

identifier ::= (letter|"_") (letter | digit | "_")*

5
“但我觉得你可以忍受多了一个“i”的情况。” 是的,这只是一个学术上的观点。 - James McMahon
1
@nemo,如果你不想使用字母数字名称,可以尝试使用for _ in range(n)。 - Unknown
1
@nemo 是一个可以接受的变量名。在解释器中,它会自动分配为您最后一次输入的表达式。 - Unknown
4
有一点需要注意,使用下划线(_)可以清楚地表明它应该被忽略。说这样做没有意义就好比说注释代码没有意义——因为实际上它们都是起到同样的作用。 - Lambda Fairy
2
使用“_”而不是“i”的另一个好处是,pylint(以及其他一些代码检查工具)会将“_”视为丢弃变量,从而避免未使用变量的警告。 - Cameron Bielstein
显示剩余7条评论

81

你可能正在寻找

for _ in itertools.repeat(None, times): ...

这是在Python中迭代times次的最快方法。


4
我并不担心性能,我只是好奇是否有更简洁的方法来编写这个语句。虽然我已经零零散散地使用 Python 大约两年了,但我仍然感觉自己缺少很多东西。Itertools 就是其中之一,谢谢你提供的信息。 - James McMahon
7
很有趣,我不知道这一点。我刚刚查看了itertools文档; 但我想知道为什么这比使用range或xrange更快? - si28719e
6
@blackkettle说的更快是因为它不需要返回当前迭代索引,这是xrange(和Python 3的range,它提供一个迭代器而不是列表)成本的可测部分。@nemo,range已经尽可能地进行了优化,但是需要构建和返回列表不可避免地比迭代器更费力(在Py3中,range确实返回迭代器,就像Py2的xrange;向后兼容性不允许在Py2中进行这样的更改),特别是不需要返回变量值的迭代器。 - Alex Martelli
5
@Cristian,是的,显然每次准备和返回一个Python整数,包括垃圾回收工作,都会有可衡量的成本--在内部使用计数器则没有问题。 - Alex Martelli
5
我现在明白了。区别在于垃圾回收开销,而不是“算法”。顺便说一下,我运行了一个快速的timeit基准测试,加速比约为1.42倍。 - Cristian Ciupitu
显示剩余6条评论

76

给不需要使用的变量赋值时,通常使用下划线 _ 命名。

for _ in range(times):
    do_stuff()

20

大家建议你使用 _,但没有说的是 _ 经常用作 gettext 函数的快捷方式。因此,如果您希望您的软件在多种语言中可用,则最好避免将其用于其他目的。

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print _('This is a translatable string.')

5
对我来说,使用下划线 _ 看起来像是一个糟糕的想法,我不介意与之发生冲突。 - KeithWM

10

这里有一个随机想法,利用(滥用?)数据模型Py3 链接)。

class Counter(object):
    def __init__(self, val):
        self.val = val

    def __nonzero__(self):
        self.val -= 1
        return self.val >= 0
    __bool__ = __nonzero__  # Alias to Py3 name to make code work unchanged on Py2 and Py3

x = Counter(5)
while x:
    # Do something
    pass

我想知道标准库中是否有类似的东西?

11
我认为拥有像 __nonzero__ 这样具有副作用的方法是一个可怕的想法。 - ThiefMaster
3
我会使用__call__代替。写while x():并不那么困难。 - Jasmijn
2
还有一个避免使用名称“Counter”的论点;当然,它不是保留的或内置范围中的名称,但collections.Counter是一种东西,并且创建一个同名的类会冒着维护者混淆的风险(尽管这已经存在这个风险)。 - ShadowRanger

7

您可以使用“_11”(或任何数字或其他无效标识符)来避免与gettext发生名称冲突。每当您使用下划线+无效标识符时,您将获得一个虚拟名称,可用于for循环。


不错!PyDev 同意您的想法:这将消除“未使用变量”的黄色警告。 - mike rodent

2
也许答案取决于您在使用迭代器时遇到的问题?也许可以使用。
i = 100
while i:
    print i
    i-=1

或者
def loop(N, doSomething):
    if not N:
        return
    print doSomething(N)
    loop(N-1, doSomething)

loop(100, lambda a:a)

但是说实话,我看不出使用这种方法的意义。

1
注意:Python(至少不是CPython的参考解释器,可能也不是大部分其他解释器)不会优化掉尾递归,因此N将被限制为sys.getrecursionlimit() 的值附近(在CPython上默认为低四位数字范围内); 使用sys.setrecursionlimit可以提高限制,但最终你会达到C堆栈限制,解释器将因堆栈溢出而死亡(不仅会引发漂亮的 RuntimeError / RecursionError)。 - ShadowRanger

0
我们可以使用while和yield,像这样创建自己的循环函数。在这里,您可以参考官方文档
def my_loop(start,n,step = 1):
    while start < n:
        yield start
        start += step

for x in my_loop(0,15):
    print(x)

0

我基本上同意以上提出的解决方案,即:

  1. for 循环中使用下划线(2 行或更多)
  2. 定义一个普通的 while 计数器(3 行或更多)
  3. 声明一个自定义类,并实现 __nonzero__ 方法(更多行)

如果要像#3中那样定义一个对象,我建议实现 with 关键字 的协议或应用 contextlib

此外,我提出了另一种解决方案。它只有 3 行代码,虽然不是最优雅的,但使用了 itertools 包,因此可能会引起兴趣。

from itertools import (chain, repeat)

times = chain(repeat(True, 2), repeat(False))
while next(times):
    print 'do stuff!'

在这些例子中,2 是循环迭代的次数。 chain 包装了两个 repeat 迭代器,第一个是有限制的,但第二个是无限的。请记住,这些都是真正的迭代器对象,因此它们不需要无限的内存。显然,这比解决方案#1要慢得多。除非作为函数的一部分编写,否则可能需要清理 times 变量。

2
“chain”是不必要的,“times = repeat(True, 2); while next(times, False):”可以达到同样的效果。 - AChampion

0

现在你有一个不必要的列表,而不是一个不必要的计数器。 最好的解决方案是使用以“_”开头的变量,这告诉语法检查器你知道自己没有使用该变量。

x = range(5)
while x:
  x.pop()
  print "Work!"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接