【问题标题】:For loops (novice)For 循环(新手)
【发布时间】:2012-05-13 12:08:42
【问题描述】:

我最近开始学习 Python,而 for 循环的概念对我来说还是有点困惑。我知道它通常遵循for x in y 的格式,其中y 只是一些列表。

for-each 循环 for (int n: someArray) 变成for n in someArray

而for循环for (i = 0; i < 9; i-=2)可以用for i in range(0, 9, -2)表示

假设我想要i*=2,甚至i*=i,而不是一个恒定的增量。这可能吗,还是我必须改用while循环?

【问题讨论】:

  • 如果第一个数字小于第二个,您的 for i in range(0,9,-2) 将不会向后迭代。
  • 更准确地说,range() 不能为正值提供负步长的数组。这是一个无限循环。
  • @user1320925 你想要这些作为 i 的值吗:1 2, 4, 8, 16, 32....
  • 如果iint 并且这是C,则后者for 循环没有正确的结束条件。无论如何,它不是您所说的范围。

标签: python loops for-loop increment


【解决方案1】:

只是作为替代方案,如何将迭代/增量操作推广到 lambda 函数,以便您可以执行以下操作:

for i in seq(1, 9, lambda x: x*2):
    print i
...
1
2
4
8

seq 定义如下:

#!/bin/python
from timeit import timeit

def seq(a, b, f):
    x = a;
    while x < b:
        yield x
        x = f(x)

def testSeq():
    l = tuple(seq(1, 100000000, lambda x: x*2))
    #print l

def testGen():
    l = tuple((2**x for x in range(27)))
    #print l

testSeq();
testGen();

print "seq", timeit('testSeq()', 'from __main__ import testSeq', number = 1000000)
print "gen", timeit('testGen()', 'from __main__ import testGen', number = 1000000)

性能上的差异并没有那么大:

seq 7.98655080795
gen 6.19856786728

[编辑]

支持反向迭代并使用默认参数...

def seq(a, b, f = None):
    x = a;
    if b > a:
        if f == None:
            f = lambda x: x+1
        while x < b:
            yield x
            x = f(x)
    else:
        if f == None:
            f = lambda x: x-1
        while x > b:
            yield x
            x = f(x)

for i in seq(8, 0, lambda x: x/2):
    print i

注意:这与 range/xrange 的行为不同,其中 &lt;/&gt; 测试的方向是由迭代器符号选择的,而不是开始值和结束值之间的差异。

【讨论】:

    【解决方案2】:

    您可以使用generator expression 高效地执行此操作,并且几乎不需要多余的代码:

    for i in (2**x for x in range(10)): #In Python 2.x, use `xrange()`.
        ...
    

    生成器表达式的工作方式类似于定义手动生成器(如Greg Hewgill's answer),其语法类似于列表推导式。它们被延迟评估 - 这意味着它们不会在操作开始时生成列表,这可以在大型可迭代对象上带来更好的性能。

    所以这个生成器的工作原理是等到它被要求一个值,然后向range(10) 要求一个值,将该值加倍,然后将其传递回for 循环。它会反复执行此操作,直到 range() 生成器不再产生任何值。

    【讨论】:

    • @TomWijsman 我认为它的可读性并没有降低。如果你真的想要你可以做doubles = (x*2 for x in range(10)) 然后循环doubles。如果您将它与您的答案进行比较 - 列表理解 - 我们实际上是在谈论表达式之外的不同括号。这怎么不可读或可维护?
    • @TomWijsman 我的回答是单行 - 并且建议单行总是比多行更好是疯狂的。有时更多的行更具可读性。至于调试更难,那根本不是真的。正如我在另一条评论中所说的那样 - 为什么所有 Python 3 的内置函数现在都在 2.x 中生成列表时变得懒惰是有原因的。
    • @Lattyware 我认为 OP 想要 2,4,8,16,32... 而您的解决方案给出了 2,4,6,8,10...。
    • @AshwiniChaudhary 哎呀,已修复。
    【解决方案3】:

    请记住,Python 的“列表”部分可以是任何可迭代的序列。

    例子:

    一个字符串:

    for c in 'abcdefg':
       # deal with the string on a character by character basis...
    

    一个文件:

    with open('somefile','r') as f:
        for line in f:
             # deal with the file line by line
    

    一本字典:

    d={1:'one',2:'two',3:'three'}
    for key, value in d.items():
       # deal with the key:value pairs from a dict
    

    列表的一部分:

    l=range(100)
    for e in l[10:20:2]:
        # ever other element between 10 and 20 in l 
    

    等等等等等等等等

    所以它确实比“只是一些列表”要深得多

    正如其他人所说,只需将可迭代设置为您想要的示例问题:

     for e in (i*i for i in range(10)):
         # the squares of the sequence 0-9...
    
     l=[1,5,10,15]
     for i in (i*2 for i in l):
         # the list l as a sequence * 2...
    

    【讨论】:

    • +1 - 这是一个很好的观点。 For 循环遍历任何可迭代对象,而不仅仅是列表。
    • 我不明白这是如何回答这个问题的。
    • @Tom Wijsman:OP 声明 the concept of for loops is still a little confusing for me. I understand that it generally follows the format for x in y, where y is just some list. 我澄清说 y 不仅仅是“只是一些列表”
    【解决方案4】:

    您需要为此使用list comprehensions

    print [x**2 for x in xrange(10)] # X to the 2nd power.
    

    print [x**x for x in xrange(10)] # X to the Xth power.
    

    列表解析语法如下:

    [EXPRESSION for VARIABLE in ITERABLE if CONDITION]
    

    在底层,它的行为类似于map and filter function:

    def f(VARIABLE): return EXPRESSION
    def c(VARIABLE): return CONDITION
    
    filter(c, map(f, ITERABLE))
    

    给出的例子:

    def square(x): return x**2
    
    print map(square, xrange(10))
    

    def hypercube(x): return x**x
    
    print map(hypercube, xrange(10))
    

    如果您不喜欢列表推导,这可以用作替代方法。 您也可以使用 for 循环,但这将远离 Python 惯用语...

    【讨论】:

    • 创建一个列表然后循环遍历它不是最优的,这也不是一个列表 comp 的语法(它是一个表达式,而不是一个函数,它是一个可迭代的,而不是一个列表,并且可以最后是一个 if 语句)。
    • @Lattyware:这是最优的,它是生成器表达式的作用。请注意您的答案如何链接到列表理解... ;)
    • 不正确。我的答案链接到我制作的视频,该视频解释了列表推导以及生成器表达式以及 dict 和集合推导。生成器表达式是惰性的,因此与列表推导式非常不同。如果我们循环到 500 万,您的解决方案将创建一个从 0*24999999*2 的列表,然后循环它。生成器表达式将根据需要计算它们。
    • 这完全正确,但在这种情况下,我们讨论的是表达式周围的一组不同的括号。这种优化不会花费我们时间、可读性或其他任何东西,那么为什么不这样做呢?当我们谈论不花费我们时间或精力的事情时,这句话就会落空,而良好的实践通常意味着当它很重要时你不需要担心。
    • 我没有对任何其他答案投反对票 - 事实上,我对其他两个答案投了反对票。现实情况是,在 Python 中使用生成器表达式进行调试很好——在 Python 中调试几乎总是使用 print() 语句完成,所以这并不重要。如果最坏的情况发生,您可以将您的生成器包裹在list() 中以查看它的输出。在使用生成器时,我在 Python 中调试了很多问题。至于保持简单 - 生成器表达式并不比列表推导复杂。
    【解决方案5】:

    正如您所说,for 循环遍历列表的元素。该列表可以包含您喜欢的任何内容,因此您可以预先构建一个包含每个步骤的列表。

    for 循环也可以迭代 "generator",这是一小段代码而不是实际列表。在 Python 中,range() 实际上是一个生成器(尽管在 Python 2.x 中,range() 返回一个列表,而xrange() 是生成器)。

    例如:

    def doubler(x):
        while True:
            yield x
            x *= 2
    
    for i in doubler(1):
        print i
    

    上面的for循环会打印出来

    1
    2
    4
    8
    

    以此类推,直到您按下 Ctrl+C。

    【讨论】:

    • 我会说一个独立的生成器对于这个来说太过分了——一个生成器表达式可能也可以做到这一点。例如:for i in (x*2 for x in range(10)):
    • 是的,你可以把代码写得更紧凑。但是,我认为展示一个通用生成器可以做什么是有启发性的。
    • 虽然很高兴知道您可以用它做更多事情,但建议人们将事情过度复杂化并不好。解释生成器表达式语法可能会很好,以避免让提问者将非常简单的生成器作为完整的函数。
    • 我鼓励您添加一个演示生成器表达式语法的答案。
    • 完成了,为你的 +1 - 这是一个很好的答案,一个可以用生成器变魔术。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多