在for循环中更改迭代次数

25

我有这样的代码:

loopcount = 3
for i in range(1, loopcount)
   somestring = '7'
   newcount = int(somestring)
   loopcount = newcount

我希望修改循环“内部”的for循环的范围。

我写了以下代码,期望在第一次循环期间for循环的范围会改变为(1,7),但这并没有发生。

相反,无论我放入什么数字,它只运行2次。(在这种情况下,我想要运行6次)

我使用以下方式检查值:

    loopcount = 3
    for i in range(1, loopcount)
       print loopcount
       somestring = '7'
       newcount = int(somestring)
       loopcount = newcount
       print loopcount
#output:
3
7
7
7

有什么问题吗?这个数字已经改变了。

我的思路出了什么问题?


是的,可以在循环内部更改范围,请参见下面的新答案。 - Raymond Hettinger
8个回答

44

这个范围是基于调用时loopcount的值创建的——之后发生在loopcount上的任何事情都是无关紧要的。你可能想要的是一个while语句:

loopcount = 3
i = 1
while i < loopcount:
    somestring = '7'
    loopcount = int(somestring)
    i += 1
while 循环测试条件 i < loopcount 是否为真,如果为真,则执行其中包含的语句。在这种情况下,每次通过循环时,i 增加 1。由于第一次是将 loopcount 设置为 7,所以循环将运行六次,对应 i=1,2,3,4,5 和 6。
一旦条件为假,即当 i = 7 时,while 循环停止执行。
(我不知道您的实际用例是什么,但您可能不需要分配新的计数器变量 newcount,因此我已将其删除)。

2
while 循环应该以 i += 1 结束,以模拟在迭代范围时的自动递增。 - Joe Day
我的for循环里有许多代码。如果我使用while和i+=1,结果会不会没有变化? - H.Choi
1
我不确定我是否理解你的问题。但是循环内部的代码将执行相同的操作,无论它是for循环还是while循环。我在答案中也更详细地解释了while循环。这有帮助吗? - Justin Blank
1
哦,它可以工作了!非常感谢你!我在这个问题上浪费了好几个小时。再次感谢你。你真的帮了我很多 :) - H.Choi
很高兴我能帮到你。这些事情变得熟能生巧可能需要一段时间。 - Justin Blank

11

range()的文档字符串中可以看到:

range([start,] stop[, step]) -> 整数列表

返回一个包含整数等差序列的列表。 range(i, j) 返回 [i, i+1, i+2, ..., j-1];(!) start 默认为 0。 当给出 step 时,它指定了增量(或减量)。 例如,range(4) 返回 [0, 1, 2, 3],终点被省略! 这些正是长度为 4 的列表的有效索引。

因此,例如 range(1, 10) 返回类似于 [1,2,3,4,5,6,7,8,9] 的列表,那么您的代码基本上执行的是:

loopcount = 3
for i in [1, 2]:
    somestring = '7'
    newcount = int(somestring)
    loopcount = newcount

当你的for循环被"初始化"时,列表会由range()函数创建出来。


啊哈!我明白了!但是我怎么改变循环中的“范围”呢?我不能这样做吗? - H.Choi
2
如果您需要修改循环边界,我建议您按照@user802500的建议使用while编码。 - Valdir Stumm Junior

5
< p > 用户 802500 给出的 while-loop 答案可能是您实际问题的最佳解决方案;但是,我认为问题 所问 具有有趣和有教益的答案。

range() 调用的结果是一系列连续的值的列表。 for-loop 迭代该列表直到耗尽为止。

这里的关键点是: 您允许在迭代期间改变列表

>>> loopcount = 3
>>> r = range(1, loopcount)
>>> for i in r:
        somestring = '7'
        newcount = int(somestring)
        del r[newcount:]

这个功能的实际应用是在待办事项列表中迭代任务,并允许一些任务生成新的待办事项:

for task in tasklist:
    newtask = do(task)
    if newtask:
        tasklist.append(newtask)

使用可变列表的好技巧。因此,我猜想,在新计数大于原始列表大小的情况下,我们将使用“append”而不是使用“del”从范围中删除一些元素。 - ntough

2

当在for循环中求解range()函数时,它会生成一个数值序列(即列表),用于迭代。

range()函数使用loopcount的值。但是,一旦该序列被生成,您在循环内部所做的任何操作都不会改变该列表,即使稍后更改loopcount的值,原始列表也将保持不变,因此迭代次数将保持不变。

在您的情况下:

loopcount = 3
for i in range(1, loopcount):

变成

for i in [1, 2]:

因为循环中有两个print语句,所以您的循环会迭代两次,您将获得4行输出。请注意,您正在打印loopcount的值,它最初为3,但后来被设置(并重置)为7。
如果您想动态更改迭代次数,请考虑改用while循环。当然,您始终可以使用break语句提前停止/退出任何循环。
另外,
   somestring = '7'
   newcount = int(somestring)

可以简化为只有
   newcount = 7

2

针对问题“我如何更改范围边界”,您可以利用生成器的send方法来解决:

def adjustable_range(start, stop=None, step=None):
    if stop is None:
        start, stop = 0, start

    if step is None: step = 1

    i = start
    while i < stop:
        change_bound = (yield i)
        if change_bound is None:
            i += step
        else:
            stop = change_bound

使用方法:

myrange = adjustable_range(10)

for i in myrange:
    if some_condition:
        myrange.send(20) #generator is now bounded at 20

2
+1,虽然这不是生成器表达式,而只是一个生成器。 - lvc
你可能想试着运行你的代码。我尝试使用这个页面上发布的另一个版本来修复你的代码。 - Noctis Skytower
在比较我们的答案之后,我有一个问题。你确定当通过 yield 传入信息时,i += step 不应该被执行吗?我的代码无条件地执行了这一步骤。 - Noctis Skytower

1

看起来你的前提是循环应该执行默认次数,但偶尔会有不同的条件。最好使用while循环,但无论如何,你可以这样做:

if i == some_calculated_threshold:
    break

从循环中退出。

1
原问题试图将循环次数从2增加到7;break应该如何帮助? - Wooble

0

一旦范围被设置,您无法增加迭代次数,但可以提前退出,从而减少迭代次数:

for i in xrange(1, 7):
   if i == 2:
       break

0

这里是 Joel Cornett 提供的 adjustable_range 函数的更完整实现。

def adjustable_range(start, stop=None, step=None):
    if not isinstance(start, int):
        raise TypeError('start')
    if stop is None:
        start, stop = 0, start
    elif not isinstance(stop, int):
        raise TypeError('stop')
    direction = stop - start
    positive, negative = direction > 0, direction < 0
    if step is None:
        step = +1 if positive else -1
    else:
        if not isinstance(step, int):
            raise TypeError('step')
        if positive and step < 0 or negative and step > 0:
            raise ValueError('step')
    if direction:
        valid = (lambda a, b: a < b) if positive else (lambda a, b: a > b)
        while valid(start, stop):
            message = yield start
            if message is not None:
                if not isinstance(message, int):
                    raise ValueError('message')
                stop = message
            start += step

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