关于python:for x in y():这是如何工作的?

for x in y(): how does this work?

本问题已经有最佳答案,请猛点这里访问。

我在寻找代码来在终端中旋转光标,找到了这个。我想知道密码里发生了什么。尤其是for c in spinning_cursor():,我从未见过这种语法。是因为我用yield每次从一个生成器返回一个元素,而这个元素被分配给c吗?对于y()中的x,还有其他的例子吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import sys
import time

def spinning_cursor():
    cursor='/-\|'
    i = 0
    while 1:
        yield cursor[i]
        i = (i + 1) % len(cursor)

for c in spinning_cursor():
    sys.stdout.write(c)
    sys.stdout.flush()
    time.sleep(0.1)
    sys.stdout.write('\b')


使用yield将函数转换为生成器。生成器是一种特殊类型的迭代器。for总是在iterables上循环,依次获取每个元素并将其分配给您列出的名称。

spinning_cursor()返回一个生成器,spinning_cursor()中的代码在开始迭代生成器之前不会实际运行。迭代一个生成器意味着函数中的代码被执行,直到它遇到一个yield语句为止,此时表达式的结果作为下一个值返回,然后再次暂停执行。

for循环就是这样做的,它会在生成器上调用等价的next(),直到生成器发出信号,通过提升StopIteration来完成(当函数返回时发生)。next()的每个返回值依次分配给c

通过在python提示符中创建上的生成器,可以看到这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> def spinning_cursor():
...     cursor='/-\|'
...     i = 0
...     while 1:
...         yield cursor[i]
...         i = (i + 1) % len(cursor)
...
>>> sc = spinning_cursor()
>>> sc
<generator object spinning_cursor at 0x107a55eb0>
>>> next(sc)
'/'
>>> next(sc)
'-'
>>> next(sc)
'\'
>>> next(sc)
'
|'

这个特定的生成器永远不会返回,所以StopIteration永远不会被提升,除非您杀死脚本,否则for循环将永远持续下去。


在Python中,for语句允许您迭代元素。

根据文件:

Python’s for statement iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence

这里,元素将是spinning_cursor()的返回值。


for c in spinning_cursor()语法是一个for-each循环。它将遍历spinning_cursor()返回的迭代器中的每个项。

循环内部将:

  • 将字符写入标准输出和刷新,以便显示。
  • 睡十分之一秒
  • 写入\b,这被解释为退格(删除最后一个字符)。注意,这发生在循环的末尾,所以在第一次迭代期间不会写入它,并且它共享步骤1中的flush调用。
  • spinning_cursor()将返回一个生成器,在开始迭代之前它不会实际运行。它看起来像是要循环通过'/-\|',顺序是,永远。这有点像有一个无限的列表需要迭代。

    所以,最终输出将是一个ASCII微调器。您将看到这些字符(在同一个位置)重复,直到您杀死脚本。

    1
    2
    3
    4
    /
    -
    \
    |

    马蒂金·彼得斯的解释很好。下面是问题中相同代码的另一个实现。它使用itertools.cycle生成与spinning_cursor相同的结果。itertools中充满了优秀的迭代器和函数示例,可以帮助您创建自己的迭代器。它可能会帮助您更好地理解迭代器。

    1
    2
    3
    4
    5
    6
    7
    import sys, time, itertools

    for c in itertools.cycle('/-\|'):
        sys.stdout.write(c)
        sys.stdout.flush()
        time.sleep(0.1)
        sys.stdout.write('\b')


    spinning_cursor函数返回一个iterable(yield中的生成器)。

    1
    for c in spinning_cursor():

    会和

    1
     for i in [1, 2, 3, 4]: