端末でカーソルを回転させるコードを探していたところ、これが見つかりました。コードで何が起こっているのかと思っていました。特にfor c in spinning_cursor():
この構文は見たことがありません。 yield
を使用してジェネレータから一度に1つの要素を返し、これがcに割り当てられているためですか? y()でのxに関する他の例はありますか?
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
を使用すると、関数が generator になります。ジェネレータは iterator の特殊なタイプです。 for
は常に反復可能オブジェクトをループし、各要素を順番に取得して、指定した名前に割り当てます。
spinning_cursor()
はジェネレータを返します。spinning_cursor()
内のコードは、ジェネレータの反復を開始するまで実際には実行されません。ジェネレーターを繰り返し処理するということは、yield
ステートメントに出会うまで関数内のコードが実行されることを意味します。この時点で、式の結果が次の値として返され、実行が再び一時停止されます。
for
ループはまさにそれを行い、ジェネレーターがStopIteration
を上げることによって完了したことを通知するまで、ジェネレーターで next()
と同等のものを呼び出します(関数が戻るときに発生します)。 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()
を使用することです:
from itertools import cycle
spinning_cursor = cycle('/-\|')
Pythonでは、forステートメントを使用して要素を反復できます。
documentation によると:
Pythonのforステートメントは、シーケンス内の項目(リストまたは文字列)を、シーケンス内に出現する順序で反復します
ここで、要素はspinning_cursor()
の戻り値になります。
for c in spinning_cursor()
構文は_for-each
_ループです。 spinning_cursor()
によって返されたイテレータの各項目を反復処理します。
ループの内側は:
\b
_を書き込みます。これは、バックスペースとして解釈されます(最後の文字を削除します)。これはループの最後で発生するため、最初の反復中には書き込まれず、ステップ1のフラッシュ呼び出しを共有していることに注意してください。spinning_cursor()
は generator を返します。これは、反復を開始するまで実際には実行されません。 _'/-\|'
_を順番に永久にループするようです。それは、反復する無限のリストを持つようなものです。
したがって、最終的な出力はASCIIスピナーになります。これらの文字が(同じスポットで)表示され、スクリプトを終了するまで繰り返されます。
_/
-
\
|
_
spinning_cursor関数は、イテラブル(yieldからのジェネレーター)を返します。
for c in spinning_cursor():
と同じになります
for i in [1, 2, 3, 4]:
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')