是否可以在没有迭代器变量的情况下为范围循环实现Python?


187

有没有可能做下面的事情i

for i in range(some_number):
    # do something

如果您只想执行N次操作并且不需要迭代器。


21
这是一个很好的问题!PyDev甚至将“ i”标记为“未使用变量”的警告。下面的解决方案删除了​​此警告。
Ashwin Nanjappa,09年

@Ashwin您可以使用\ @UnusedVariable删除该警告。请注意,我需要转义“ at”符号才能通过此注释。
拉菲·哈查杜安

我向你问同样的问题。带有pylint警告,这很烦人。当然,您可以通过其他抑制来禁用警告,例如@Raffi Khatchadourian建议。最好避免出现pylint警告抑制注释。
探戈

Answers:


109

在我头顶上,没有。

我认为您可以做的最好的事情是这样的:

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

因此,我不会以这种方式使用它。我不知道瑞安提到的任何成语。它会弄乱您的口译员。

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

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

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

4
“但是我认为您可以只接受多余的“ i”。”是的,这只是一个学术观点。
詹姆斯·麦克马洪

1
@nemo,如果您不想使用字母数字名称,则可以尝试在range(n)中使用_。
未知

在这种情况下_是变量吗?还是Python中还有其他东西?
詹姆斯·麦克马洪

1
@nemo是的,它只是一个可接受的变量名。在解释器中,将自动为其分配您最后创建的表达式。
未知

3
@kurczak有一点。使用_表明它应该被忽略。说这样做是没有意义的,就像说注释代码没有意义-因为它仍然会完全一样。
Lambda Fairy

69

您可能正在寻找

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

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


2
我并不关心性能,我只是好奇是否有一种更简短的方式来编写该语句。虽然我已经零星地使用Python大约2年了,但是我仍然觉得我缺少很多东西。Itertools就是其中之一,谢谢您提供信息。
詹姆斯·麦克马洪

5
很有意思,我没有意识到。我只是看了一下itertools文档;但我想知道为什么这比仅使用range或xrange更快?
si28719e 2009年

5
@blackkettle:它更快,因为它不需要返回当前的迭代索引,这是xrange(和Python 3的范围,它提供一个迭代器,而不是列表)成本的可衡量部分。@nemo,范围已尽可能优化,但需要构建和返回列表比迭代器难免繁重的工作(在Py3中,范围确实返回了迭代器,如Py2的xrange;向后兼容不允许这种更改在Py2中),尤其是不需要返回变化值的代码。
亚历克斯·马蒂利

4
@Cristian,是的,每次都明确准备并返回一个Python int,inc。gc的工作,确实有可衡量的成本-不必在内部使用计数器。
Alex Martelli

4
我现在知道了。区别来自GC开销,而不是来自“算法”。顺便说一句,我运行了一个快速的timeit基准测试,加速约为1.42倍。
Cristian Ciupitu 09年

59

分配未使用的值的一般习惯是命名它_

for _ in range(times):
    do_stuff()

18

每个人都建议您使用_并不是说_经常被用作gettext函数之一的快捷方式,因此,如果您希望您的软件以一种以上的语言提供,那么最好避免使用它用于其他目的。

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

在我_看来,这种用法似乎是一个可怕的主意,我不介意与之冲突。
KeithWM

9

这是一个利用(滥用?)数据模型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

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


10
我认为,具有__nonzero__副作用的方法是一个可怕的想法。
ThiefMaster 2012年

2
我会__call__改用。while x():编写起来并不难。
Jasmijn 2012年

1
还有一个避免使用名字的论点Counter; 当然,它不是保留的,也不在内置范围内,而是collections.Counter一件事,并且使同名类具有维护者混乱的风险(并不是说这还没有冒这个风险)。
ShadowRanger

7

您可以使用_11(或任何数字或其他无效标识符)来防止与gettext的名称冲突。每当您使用下划线+无效标识符时,都会获得一个可用于for循环的虚拟名称。


真好!PyDev同意您的观点:这消除了“未使用的变量”黄色警告。
麦克·

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


1

现在您有了不需要的列表,而不是不需要的计数器。最佳解决方案是使用以“ _”开头的变量,该变量告诉语法检查程序您知道自己没有使用该变量。

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

0

我通常同意上面给出的解决方案。即具有:

  1. for-loop中使用下划线(2行及更多行)
  2. 定义普通while计数器(3行及更多行)
  3. 声明带有__nonzero__实现的自定义类(多行)

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

此外,我提出了另一种解决方案。它是3衬管,并不是至高无上的,但是它使用了itertools包装,因此可能引起关注。

from itertools import (chain, repeat)

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

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


2
chain是不必要的,times = repeat(True, 2); while next(times, False):做同样的事情。
AChampion 2015年

0

以下内容使我们很开心,很有趣,可以分享一下:

class RepeatFunction:
    def __init__(self,n=1): self.n = n
    def __call__(self,Func):
        for i in xrange(self.n):
            Func()
        return Func


#----usage
k = 0

@RepeatFunction(7)                       #decorator for repeating function
def Job():
    global k
    print k
    k += 1

print '---------'
Job()

结果:

0
1
2
3
4
5
6
---------
7

0

如果do_something是一个简单函数或可以包装为一个函数,则简单map()可以执行以下操作do_something range(some_number)

# Py2 version - map is eager, so it can be used alone
map(do_something, xrange(some_number))

# Py3 version - map is lazy, so it must be consumed to do the work at all;
# wrapping in list() would be equivalent to Py2, but if you don't use the return
# value, it's wastefully creating a temporary, possibly huge, list of junk.
# collections.deque with maxlen 0 can efficiently run a generator to exhaustion without
# storing any of the results; the itertools consume recipe uses it for that purpose.
from collections import deque

deque(map(do_something, range(some_number)), 0)

如果要将参数传递给do_something,则可能还会发现itertools repeatfunc配方用法很不错:

要传递相同的参数:

from collections import deque
from itertools import repeat, starmap

args = (..., my args here, ...)

# Same as Py3 map above, you must consume starmap (it's a lazy generator, even on Py2)
deque(starmap(do_something, repeat(args, some_number)), 0)

传递不同的参数:

argses = [(1, 2), (3, 4), ...]

deque(starmap(do_something, argses), 0)

-1

如果您确实想避免放置带有名称的内容(OP中的迭代变量,或者不需要的列表或不需要的生成器返回所需时间的真实值),则可以按照实际情况进行操作:

for type('', (), {}).x in range(somenumber):
    dosomething()

使用的技巧是创建一个匿名类type('', (), {}),该类将导致一个具有空名称的类,但是请注意,它不会插入本地或全局名称空间中(即使提供了非空名称)。然后,使用该类的成员作为不可访问的迭代变量,因为它所属的类是不可访问的。


显然,这是故意造成的,因此批评它是没有意义的,但是我会在这里指出一个额外的陷阱。在CPython(引用解释器)上,类定义自然是循环的(创建类不可避免地会创建一个引用周期,以防止基于引用计数进行确定性的类清除)。这意味着您正在等待循环GC来启动并清理课程。它通常会作为年轻一代的一部分进行收集,默认情况下会经常收集它,但是即使如此,每个循环也意味着约1.5 KB的垃圾(带有不确定的生命周期)。
ShadowRanger

基本上,为了避免在每个循环中(通常是)确定性地清除(当其反弹并清除旧值时)命名的变量,您要制作一个巨大的未命名变量,并不确定性地对其进行清理。持续较长时间。
ShadowRanger


-7

关于什么:

while range(some_number):
    #do something

3
这是一个无限循环,因为条件range(some_number)始终为真!
致命的2012年

@deadly:好吧,如果some_number小于或等于0,它不是无限的,就永远不会运行。:-)对于无限循环(尤其是在Py2上),它的效率相当低,因为它会为每个测试创建一个新的list(Py2)或range对象(Py3)(从解释器的角度来看,它不是常数,因此必须加载range并加载some_number每个循环,请调用range,然后测试结果)。
ShadowRanger
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.