2017-12-22 24 views
0

python3で、なぜ次のコードで数字0,1,2,3,4が出力されるのですか?python3でリストの解説とジェネレータを使用して印刷

[print(i) for i in range(5)] 

いますが、それは何もしない発電機と同じことを行う場合:

(print(i) for i in range(5)) 

を使用すると、発電機と同じ結果を得ることができる唯一の方法は次のようにそれを反復することですこの:

z = (print(i) for i in range(5)) 
for i in z: 
    i 
+0

この[質問](https://stackoverflow.com/questions/47789/generator-expressions-vs-list-comprehension)をご覧ください –

+0

@GarbageCollectorご回答いただきありがとうございます。私はこの質問を見て、私はリストとジェネレータの違いを知っていると言わなければなりません。私の場合、リストまたはジェネレータに要素を入れるだけではなく、関数を呼び出すことになります。私は実際に関数を呼び出す最初のものを引き起こすバックグラウンドで起こっていることを知りたい。 –

+1

'print'の代わりに' f'という関数をいくつか実行すると時間がかかります。ジェネレータを使用する理由は、レイジー・アベレージを利用することです。レイジー・アベレージは、必要のない計算を行わないためです。私たちが 'z =(f(i)for i in range(5)') 'を書くとき、私たちはジェネレーターの理解度を設定していますが、実際にはそれが説明する計算は行っていません。遅延評価と、 'def'構文を使って独自のジェネレータをPythonで記述する方法についてもっと研究することをお勧めします。 –

答えて

2

ジェネレータ式によって返されるオブジェクトなどのジェネレータは、遅延イテレータです。あなたが求める値を提供するのに必要なだけのコードを実行します。ジェネレータを反復処理しない場合は、値を要求しないので、コードを実行しないため、何も出力されません。

あなたがそれにnextを呼び出すことで、イテレータから個々の値を要求することができます。

gen = (print(i) for i in range(5)) 

x = next(gen) # causes 0 to be printed 
y = next(gen) # causes 1 to be printed 

nextの最初の呼び出しは、発電機からの最初の値を要求し、それはrangeからの最初の値でprintを呼び出します。 nextへの2回目の呼び出しでは、rangeの2番目の値が出力されます。 xyの両方にNoneが割り当てられます。これはprintが返すものであるため、ジェネレータによって生成されるものです。

あなたはforループで発電機を置く(または引数を反復listのような関数に渡す)、全体の発電機が消費される場合は、ちょうどあなたが回の束、それにnextを呼び出した場合などです。 nextを手作業で呼び出すと、最終的にStopIterationという例外が発生します。これは、イテレータがそれ以上の値を返さないことを通知する方法です。

ジェネレータとは異なり、リストの理解は怠惰ではないため、試したリストの理解には同じ動作がありませんでした。すぐにすべてのコードを実行し、取得したすべての値からリストを作成します。あなたのリストの理解はNoneの値で完全なリストを作成します(それはprintが返すものなので)。そして、リストがいっぱいになるとすべての数字が印刷されます。

コードのバージョンはどちらも非常にPythonicではありません。一般的には、副作用(例えば、print値など)に対してのみ、ジェネレータ式またはリスト内包を使用しないでください。あなたが得られた値を気にするとき(またはリストに入れて)、それらを使用してください。あなただけの番号の範囲を印刷したい場合は、単に直接範囲にforループを使用し、ループの本体でprintを呼び出す:

for i in range(5): 
    print(i) 

それは持っていない許容可能なリスト内包またはコードとジェネレータ式です副作用として、計算される値が有用である限り。たとえば、あなたが信頼性の低いネットワーク上のクライアントの束にメッセージを送信したい場合、あなたはこのような何かを見ていくつかのコードを使用する可能性があります:

results = [client.send(message) for client in clients] # client.send may return an error code 
for result in results: # process the results after all the messages were sent 
    if result is not None: # no error means success 
     print("client.send error:", result) # report failures, but keep going 

最初の行にリストの内包は、主に使用されていますその副作用(メッセージを送信する)が返されますが、返された値のリストは有用なものです。何故エラーが発生したのかが分かるからです。

+0

基本的な入力ミスを修正しました。 :) –

+0

@MadPhysicist:ありがとう! – Blckknght

2

あなたは

(print(i) for i in range(5)) 
でジェネレータオブジェクトを作成しているためです

リストはありません。

list(print(i) for i in range(5)) 

に変更すると、最初の文と同じ動作をします。

ジェネレータは、特別なタイプのイテレータです。詳細については、Pythonのマニュアルhereを参照してください。

括弧の間にあるコード(print(i) for i in range(5))は、反復処理が終わってから実際にはprint関数を呼び出すことはありません。リストはリストの理解のためにプリント機能を呼び出す。 [print(i) for i in range(5)]またはlist(print(i) for i in range(5))を使用することにより、リストの内包に

for i in range(5): 
    print i 

に相当します。つまり、あなたは実際にprint関数を呼び出しています。リストの理解の詳細については、thisウェブサイトをご覧ください。

+0

しかし、なぜこれが起こっていますか?ジェネレータがプリント機能を呼び出すのはなぜですか? –

+1

ジェネレータは、 'iterator'オブジェクトを作成しているので、print関数を呼び出しません。私の編集をチェックしてください。 – B3W

+0

@ B3W。ジェネレータは、反復を要求するまで何も行いません。これは、遅延処理の性質とジェネレータの全目的です。 –

関連する問題