リスト内包表記のポイントは、結果値のリストを、ソース値ごとに 1 つ (句がある場合は、一致するソース値ごとに 1 つ) 生成することです。if
言い換えれば、 (複数の句がある場合はand呼び出しmap
のチェーン)と同じですが、新しい値をそれぞれ古い値の観点から式として記述できる点が異なります。機能。map
filter
mysum = mysum + i
ステートメント ( など) を内包表記に入れることはできません。式のみです。そして、たとえあなたが望む副作用を持つ式を考え出すことができたとしても、それは依然として混乱を招く理解の誤用です. 結果値のリストが必要ない場合は、リスト内包表記を使用しないでください。
ループで計算を実行しようとしているだけの場合は、明示的なfor
ループを記述します。
本当に1行にする必要がある場合は、いつでもこれを行うことができます:
for i in [i for i in range(2, 10) if i%2==0 or i%5==0]: mysum += i
内包でループするもののリストを作成します。for
ループで副作用 y の計算を行います。
(もちろん、これはmysum
、たとえば を使用して、追加する値を既に持っていることを前提としていますmysum = 0
。)
そして、一般に、一度ループするためだけに内包表記が必要な場合はいつでも、必要な種類の内包表記はリスト内包表記ではなく、ジェネレーター式です。したがって、これらの角括弧を括弧に変えると、次のようになります。
for i in (i for i in range(2, 10) if i%2==0 or i%5==0): mysum += i
ただし、いずれにしても、次の 2 行の方が読みやすく、pythonic です。
for i in (i for i in range(2, 10) if i%2==0 or i%5==0):
mysum += i
…または 3 つ:
not2or5 = (i for i in range(2, 10) if i%2==0 or i%5==0)
for i in not2or5:
mysum += i
reduce
ループよりも/fold
をより直感的にする言語から来ている場合、Pythonにはreduce
機能があります。for
ただし、ループを排除してブロックステートメントをワンライナーにするためだけに使用することは、一般的に Pythonic とは見なされません。
より一般的に言えば、物事を 1 行に詰め込もうとすると、通常、Python では読みにくくなります。多くの場合、頭の中で入力する文字と処理するトークンが増えることになり、利益が相殺されます。ラインを節約します。
もちろん、この特定のケースでは、本当にやりたいことはリストの値を合計することだけです。そして、それはまさにそれsum
です。そして、それを理解するのは簡単です。そう:
mysum += sum(i for i in range(2, 10) if i%2==0 or i%5==0)
mysum
(繰り返しますが、これは、既に追加したいものが にあることを前提としています。そうでない場合は、 を に変更し+=
て=
ください。以降のすべての例についても同じことが当てはまるので、説明をやめます。)
そうは言っても、私はおそらくこれを明示的なネストされたブロックとして書くでしょう:
for i in range(2, 10):
if i%2==0 or i%5==0:
mysum += i
…またはイテレータ変換のシーケンスとして (この場合、実際には 1 つの変換にすぎません):
not2or5 = (i for i in range(2, 10) if i%2==0 or i%5==0)
mysum += sum(not2to5)
(リスト内包表記の代わりにジェネレーター式を使用する限り) この方法で物事を分割するコストは実際にはなく、通常はコードの意図がより明確になります。
ジェネレーター式に関する詳細な説明:
ジェネレータ式は、リストの代わりにイテレータを作成することを除いて、リスト内包表記と同じです。イテレータは、一度しか使用できないことを除いて、一部の関数型言語の「レイジー リスト」に似ています。(通常、これは問題ではありません。上記のすべての例で、sum
関数に渡すか、for
ループで使用することだけが目的であり、それを再度参照することはありません。)各値はオンデマンドで構築され、次の値に到達する前に解放されます。
これは、スペースの複雑さが線形ではなく一定であることを意味します。一度にメモリ内に取得できる値は 1 つだけですが、リストを使用すると明らかにすべての値を取得できます。それはしばしば大きな勝利です。
ただし、時間の複雑さは変わりません。リスト内包表記はすべての作業を前もって行うため、構築する時間は直線的で、その後は自由に使用できます。ジェネレーター式は、反復処理を行うときに機能するため、自由に構築でき、線形に使用できます。いずれにせよ、同じ時間。(ジェネレータ式は、実際にはキャッシュ/メモリの局所性、パイプラインなどにより、実際には大幅に高速になる可能性があり、メモリの移動と割り当てのコストをすべて回避することは言うまでもありません。一方、少なくともリストの迅速な特殊ケースではなく、完全なイテレータ プロトコルを使用する必要があるためです。)
(ここでは、各ステップの作業は一定であると仮定しています。明らかに[sum(range(i)) for i in range(n)]
、n については 2 次であり、線形ではありません…)