これはより概念的な質問です。 Python(これは2.7で動作し、2.5でも実行された可能性があります)でfor
ループが同じ名前を使用するコードを最近見ました繰り返し処理されていたリストとリスト内の項目の両方について、これは私が悪い習慣であり、まったく機能してはならないことの両方であると感じます。
例えば:
x = [1,2,3,4,5]
for x in x:
print x
print x
収量:
1
2
3
4
5
5
今、出力された最後の値がループからxに割り当てられた最後の値になることは私には理にかなっていますが、for
ループし、意図したとおりに機能させます。それらは異なるスコープにありますか?このような何かを機能させるフードの下で何が起こっていますか?
dis
の意味:
_Python 3.4.1 (default, May 19 2014, 13:10:29)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from dis import dis
>>> dis("""x = [1,2,3,4,5]
... for x in x:
... print(x)
... print(x)""")
1 0 LOAD_CONST 0 (1)
3 LOAD_CONST 1 (2)
6 LOAD_CONST 2 (3)
9 LOAD_CONST 3 (4)
12 LOAD_CONST 4 (5)
15 BUILD_LIST 5
18 STORE_NAME 0 (x)
2 21 SETUP_LOOP 24 (to 48)
24 LOAD_NAME 0 (x)
27 GET_ITER
>> 28 FOR_ITER 16 (to 47)
31 STORE_NAME 0 (x)
3 34 LOAD_NAME 1 (print)
37 LOAD_NAME 0 (x)
40 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
43 POP_TOP
44 JUMP_ABSOLUTE 28
>> 47 POP_BLOCK
4 >> 48 LOAD_NAME 1 (print)
51 LOAD_NAME 0 (x)
54 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
57 POP_TOP
58 LOAD_CONST 5 (None)
61 RETURN_VALUE
_
重要なビットはセクション2とセクション3です。x
(24 LOAD_NAME 0 (x)
)から値をロードし、そのイテレーター(_27 GET_ITER
_)を取得して、そのイテレーター(_28 FOR_ITER
_)。 Pythonイテレータを再度ロードするために戻ることはありません。
余談:すでにイテレータを持っているので、そうすることは意味がありません Abhijitが彼の答えで指摘しています 、 Pythonの仕様のセクション7. は実際にこの動作を必要とします)。
以前はx
と呼ばれていたリスト内の各値を指すようにx
という名前が上書きされる場合、Pythonはイテレータを見つけるのに問題はありません。 x
という名前をもう一度見て、反復プロトコルを終了します。
コアリファレンスとしてサンプルコードを使用する
x = [1,2,3,4,5]
for x in x:
print x
print x
マニュアルのセクション 7.3。forステートメント を参照してください。
抜粋1
式リストは1回評価されます。反復可能なオブジェクトを生成する必要があります。 expression_listの結果に対して反復子が作成されます。
つまり、変数x
は、オブジェクトの記号名ですlist
:[1,2,3,4,5]
は、反復可能なオブジェクトとして評価されます。 expression-list が再度評価されないため、変数、シンボリック参照がその忠誠を変更する場合でも、すでに評価および生成された反復可能オブジェクトへの影響はありません。
注
抜粋2
次に、イテレーターによって提供された項目ごとに、インデックスの昇順でスイートが1回実行されます。
ここで、スイートは式リストではなくイテレーターを指します。したがって、反復ごとに、元の式リストを参照する代わりに、反復子が実行されて次のアイテムが生成されます。
考えてみれば、このように動くことが必要です。 for
ループのシーケンスの式は何でもかまいません。
_binaryfile = open("file", "rb")
for byte in binaryfile.read(5):
...
_
ループを通過するたびにシーケンスをクエリすることはできません。または、5バイトのnextバッチから2回目に読み取ります。 。当然、Pythonは、ループが始まる前に、何らかの形で式の結果をプライベートに格納する必要があります。
それらは異なるスコープにありますか?
いいえ。これを確認するには、元のスコープディクショナリ( locals() )への参照を保持し、ループ内で実際に同じ変数を使用していることに注意してください。
_x = [1,2,3,4,5]
loc = locals()
for x in x:
print locals() is loc # True
print loc["x"] # 1
break
_
このような何かを機能させるフードの下で何が起こっていますか?
Sean Vieira は内部で何が行われているのかを正確に示しましたが、より読みやすく説明するためにpythonコードでは、for
ループは基本的にこのwhile
ループ:
_it = iter(x)
while True:
try:
x = it.next()
except StopIteration:
break
print x
_
これは、古いバージョンのJavaで見られる、反復に対する従来のインデックス作成アプローチとは異なります。次に例を示します。
_for (int index = 0; index < x.length; index++) {
x = x[index];
...
}
_
アイテム変数とシーケンス変数が同じ場合、このアプローチは失敗します。これは、x
が最初に再割り当てされた後、シーケンスx
を使用して次のインデックスを検索できなくなるためです。最初のアイテム。
ただし、前者のアプローチでは、最初の行(it = iter(x)
)は iteratorオブジェクト を要求します。 x
が最初にポイントしていたシーケンスは、直接アクセスする必要がなくなりました。
変数(x)とそれが指すオブジェクト(リスト)の違いです。 forループが開始すると、Pythonは、xが指すオブジェクトへの内部参照を取得します。これはオブジェクトを使用しますが、xが参照するものは常に使用しません。
Xを再割り当てしても、forループは変わりません。 xが変更可能なオブジェクト(例:リスト)を指し、そのオブジェクトを変更(例:要素を削除)した場合、結果は予測できません。
基本的に、forループはリストx
を受け取り、それを一時変数として格納しますreは、その一時変数の各値にx
を割り当てます。したがって、x
がリストの最後の値になります。
_>>> x = [1, 2, 3]
>>> [x for x in x]
[1, 2, 3]
>>> x
3
>>>
_
このように:
_>>> def foo(bar):
... return bar
...
>>> x = [1, 2, 3]
>>> for x in foo(x):
... print x
...
1
2
3
>>>
_
この例では、x
はfoo()
にbar
として格納されているため、x
は再割り当てされていますが、foo()
を使用して、for
ループをトリガーすることができます。
x
が元のx
リストを参照しなくなったため、混乱が生じません。基本的に、pythonは元のx
リストを反復していることを覚えていますが、反復値(0、1、2など)をx
に割り当て始めるとすぐに、それは参照しなくなります元のx
リスト。名前は反復値に再度割り当てられます。
In [1]: x = range(5)
In [2]: x
Out[2]: [0, 1, 2, 3, 4]
In [3]: id(x)
Out[3]: 4371091680
In [4]: for x in x:
...: print id(x), x
...:
140470424504688 0
140470424504664 1
140470424504640 2
140470424504616 3
140470424504592 4
In [5]: id(x)
Out[5]: 140470424504592