Для x в y(): как это работает?
Я искал код для вращения курсора в терминале и нашел это. Мне было интересно, что происходит в коде. В частности, for c in spinning_cursor():
я никогда не видел этот синтаксис. Это потому, что я возвращаю один элемент из генератора за раз с yield
, и ему присваивается c? Любые другие примеры этого для x в y() используют?
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')
Ответы
Ответ 1
Использование yield
превращает функцию в генератор. Генератор - это специализированный тип итератора. for
всегда проходит по итерируемым элементам, беря каждый элемент по очереди и присваивая его названию, которое вы перечислили.
spinning_cursor()
возвращает генератор, код внутри spinning_cursor()
самом деле не запускается, пока вы не начнете перебирать генератор. Итерация по генератору означает, что код в функции выполняется до тех пор, пока не встретит оператор yield
, после чего результат выражения там возвращается как следующее значение, и выполнение снова приостанавливается.
Цикл for
делает именно это, он будет вызывать эквивалент next()
в генераторе, пока генератор не StopIteration
(что происходит, когда функция возвращается). Каждое возвращаемое значение next()
, в свою очередь, присваивается c
.
Вы можете убедиться в этом, создав генератор в приглашении Python:
>>> 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
будет продолжаться вечно, пока вы не убьете скрипт.
itertools.cycle()
более скучной (но более эффективной) альтернативой было бы использование itertools.cycle()
:
from itertools import cycle
spinning_cursor = cycle('/-\|')
Ответ 2
В Python оператор for позволяет перебирать элементы.
По документации:
Питон для оператора перебирает элементы любой последовательности (список или строка) в том порядке, в котором они появляются в последовательности
Здесь элементом будет возвращаемое значение spinning_cursor()
.
Ответ 3
Синтаксис for c in spinning_cursor()
- это цикл for-each
. Он будет проходить через каждый элемент в итераторе, возвращенный spinning_cursor()
.
Внутренняя часть цикла будет:
- Введите символ в стандартный и скрытый, чтобы он отображался.
- Сон на десятую часть секунды
- Напишите
\b
, который интерпретируется как обратное пространство (удаляет последний символ). Обратите внимание, что это происходит в конце цикла, поэтому он не будет записан во время первой итерации и что он разделяет вызов flush на шаге 1.
spinning_cursor()
собирается вернуть generator, который фактически не запускается до тех пор, пока вы не начнете выполнять итерацию. Похоже, он будет проходить через '/-\|'
, чтобы, навсегда. Это похоже на бесконечный список итераций.
Таким образом, конечный результат будет AinnerII. Вы увидите, что эти символы (в том же месте) повторяются до тех пор, пока вы не убьете script.
/
-
\
|
Ответ 4
функция spinning_cursor возвращает итерабельность (генератор с выходом).
for c in spinning_cursor():
будет таким же, как
for i in [1, 2, 3, 4]:
Ответ 5
Описание Martijn Pieters отличное. Ниже приведена другая реализация того же кода, что и у вас в вопросе. он использует itertools.cycle для получения того же результата, что и spinning_cursor
. itertools заполнен отличными примерами итераторов и функций, которые помогают создавать собственные итераторы. Это может помочь вам лучше понять итераторы.
import sys, time, itertools
for c in itertools.cycle('/-\|'):
sys.stdout.write(c)
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')