不在任何地方存储迭代次数的迭代特定次数

60

我想知道是否可能执行一定数量的操作而不在任何地方存储循环迭代数。

例如,假设我想要在控制台上打印两个“hello”消息。目前我知道可以这样做:

for i in range(2):
    print "hello"

但是,i变量将会取值为01(我实际上并不需要这些值)。有没有一种方法可以在不存储这些不需要的值的情况下实现同样的功能?

不用说,使用变量根本不是什么大问题......我只是好奇。

9个回答

56

在编程中,一个未使用的变量惯用语(被很多其他语言共享)是一个下划线 _ 。代码分析器通常不会抱怨 _ 没有使用,并且程序员立即知道它是 i_dont_care_wtf_you_put_here 的快捷方式。没有办法进行迭代而没有项目变量 - 正如 Python 之禅所说,“特殊情况并不足以打破规则”。


4
我不明白为什么像AutoHotkey的Loop, 5 { ...}这样的东西(简单地重复一个动作5次)在其他地方不存在。不需要使用变量,同时提高可读性。 - phil294
下划线也被用于gettext i18n和其解释器预定义变量中。 i看起来更好。 - ubombi
@phil294 我想有时候如果你在重构代码,你可能需要这个变量,所以更改它的名称比更改整个循环要容易些。 - wjandrea

20
exec 'print "hello";' * 2

应该可以工作,但我有点感到羞愧,因为我想到了这个方法。

更新:我又想到了另一个方法:

for _ in " "*10: print "hello"

18

我认为你在问题中提供的for循环已经是最好的了,但我想指出未使用的变量可以分配给名为_的变量,这是一种“丢弃”所分配值的约定。尽管_引用将保存您赋予它的值,但代码 linters 和其他开发人员将了解您未使用该引用。因此这是一个例子:

for _ in range(2):
    print('Hello')

8

其他人已经讨论了在for循环中无法完全避免迭代变量的问题,但有一些选项可以减少一点点工作量。毕竟,range必须生成大量数字,这涉及一点点工作;如果你想避免甚至这一点点工作,你可以使用itertools.repeat只是不断地获得相同(被忽略的)值,这不涉及创建/检索不同的对象:

from itertools import repeat

for _ in repeat(None, 200):  # Runs the loop 200 times
    ...

这将在微基准测试中比for _ in range(200):运行得更快,但如果循环体执行有意义的工作,则这只是杯水车薪。而且,与将某些匿名序列乘以您的循环可迭代对象不同,repeat仅具有微不足道的设置成本,并且没有与长度相关的内存开销。

5

虽然我完全同意delnan的回答,但这并非不可能:

loop = range(NUM_ITERATIONS+1)
while loop.pop():
    do_stuff()

请注意,对于任意列表,这种方法是行不通的:如果列表中的第一个值(最后一个弹出的值)不评估为 False,您将得到另一个迭代,并在下一次通过时引发异常:IndexError: pop from empty list。此外,在循环之后,你的列表(loop)将会变为空。仅供好奇参考。;)

警告,这只适用于Py2。在Py3中,“range”是一个专门的不可变序列类型,而不是“list”。话虽如此,我确实欣赏为了聪明才智而做出的努力,所以为了帮忙,这里是针对Py3.2+的工作变体(避免制作“list”的开销,因此更有效率)。首先,使用loop = reversed(range(NUM_ITERATIONS + 1))进行初始化,然后使循环打开while next(loop):。通过使用范围对象上的反向迭代器(按需生成它们而不是急切地提前生成它们),将产生相同顺序的相同值。 - ShadowRanger
Py2 版本也可以使用 xrange,但它略微丑陋(xrange 不支持反向迭代,因此您必须手动提供所有三个参数,并以相反的顺序操作,这看起来有点丑陋以设置结束点):初始化将是 loop = iter(xrange(NUM_ITERATIONS, -1, -1)),而 while 与 Py3 中相同(至少在 2.6+ 上;在 2.6 之前,您需要执行 while loop.next():,但希望即使在 2.7 结束支持两年后,也没有人仍在使用 2.5 或更早版本)。 - ShadowRanger

3
这将会打印出“hello”三次而不存储i
[print('hello') for i in range(3)]

2
这与原始代码相同,只是以列表推导式的形式呈现,而不是使用for循环。 - Thierry Lathuille
1
我认为i在列表推导式之外将不存在,这与for循环不同。 - Sam Hartman
@SamHartman:在Py2中,i将存在于列表推导之外,但在Py3中不存在。在Py2中,列表推导没有单独的作用域,但在Py3中(以及对于Py2上的genexprs和setcomp/dictcomps),它们在闭包作用域中运行,其中迭代变量是局部于闭包作用域的,不能在其外部看到。列表推导与所有其他推导/genexprs之间的差异是Py2和Py3之间简化(以兼容性破坏方式)的事情之一。 - ShadowRanger

2

抱歉,为了在任何语言中进行迭代,包括Python和英语,都必须存储索引。无论是否在变量中。找到一种方式来隐藏Python内部跟踪for循环的事实并不会改变它的事实。我建议您将其保留原样。


1
你可以简单地做到:
print 2*'hello'

2
这只是针对OP提供的_example_的解决方案,不能回答所有情况。 - Nae

1
for word in ['hello'] * 2:
    print word

虽然这不是 Python 的习惯用法,但你试图做的也不是。


例如,您需要一个未使用的变量来生成随机数列表。 - wRAR
问题在于,这个变量实际上并不是未使用的。必须有人来跟踪迭代的次数以及循环已执行的次数。 - nmichaels
@Nathon:不对。由于“for i in seq:stuff()”等效于(请原谅我使用大括号,但注释不保留换行符)“_iterator = iter(seq);while True { try { i = next(_iterator)} except StopIteration { break } stuff() }”,因此我们可以从去糖化版本中删除“i =”部分,最终得到没有迭代变量的结果。所有迭代状态都由迭代器管理,而不是由迭代变量管理。 - user395760
@delnan 我的意思是在底层实现中。即使迭代器类将其隐藏在某个地方,实现仍然需要跟踪状态。无论如何都会有一些内存用于跟踪迭代的位置。它可能无法访问,但这并不意味着它不存在。 - nmichaels
@nmichaels:即使在CPU寄存器的内部,您也可以迭代而不需要隐藏任何迭代变量,例如 def g(): { while True: { if random.random() < 0.1: { break } else { yield 1 } } } }g() 是一个生成器; 在任何给定时间,迭代停止的机会为1/10,且没有任何迭代变量(严格来说random.random()具有内部状态,您可以认为它是迭代变量的替代,但我们可以根据外部源(例如http://www.random.org/)实现`random.random()`,在这种情况下,没有东西可以预测迭代何时停止)。 - jfs
@J.F. Sebastian: 好的,可以。但这有什么好处?假设你想要确定性行为,必须在某个层面上维护一些状态来确定该行为。我认为这个假设并不牵强。 - nmichaels

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