web-dev-qa-db-ja.com

Python forループでイテレータとシーケンスに同じ名前を使用できるのはなぜですか?

これはより概念的な質問です。 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ループし、意図したとおりに機能させます。それらは異なるスコープにありますか?このような何かを機能させるフードの下で何が起こっていますか?

78
Gustav

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です。x24 LOAD_NAME 0 (x))から値をロードし、そのイテレーター(_27 GET_ITER_)を取得して、そのイテレーター(_28 FOR_ITER_)。 Pythonイテレータを再度ロードするために戻ることはありません

余談:すでにイテレータを持っているので、そうすることは意味がありません Abhijitが彼の答えで指摘していますPythonの仕様のセクション7. は実際にこの動作を必要とします)。

以前はxと呼ばれていたリスト内の各値を指すようにxという名前が上書きされる場合、Pythonはイテレータを見つけるのに問題はありません。 xという名前をもう一度見て、反復プロトコルを終了します。

67
Sean Vieira

コアリファレンスとしてサンプルコードを使用する

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 が再度評価されないため、変数、シンボリック参照がその忠誠を変更する場合でも、すでに評価および生成された反復可能オブジェクトへの影響はありません。

  • Pythonのすべてはオブジェクトであり、識別子、属性、メソッドを持っています。
  • 変数はシンボリック名であり、特定のインスタンスで1つだけのオブジェクトへの参照です。
  • 実行時の変数は、その忠誠を変更する可能性があります。つまり、他のオブジェクトを参照できます。

抜粋2

次に、イテレーターによって提供された項目ごとに、インデックスの昇順でスイートが1回実行されます。

ここで、スイートは式リストではなくイテレーターを指します。したがって、反復ごとに、元の式リストを参照する代わりに、反復子が実行されて次のアイテムが生成されます。

42
Abhijit

考えてみれば、このように動くことが必要です。 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が最初にポイントしていたシーケンスは、直接アクセスする必要がなくなりました。

5
nmclean

変数(x)とそれが指すオブジェクト(リスト)の違いです。 forループが開始すると、Pythonは、xが指すオブジェクトへの内部参照を取得します。これはオブジェクトを使用しますが、xが参照するものは常に使用しません。

Xを再割り当てしても、forループは変わりません。 xが変更可能なオブジェクト(例:リスト)を指し、そのオブジェクトを変更(例:要素を削除)した場合、結果は予測できません。

4
tdelaney

基本的に、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
>>> 
_

この例では、xfoo()barとして格納されているため、xは再割り当てされていますが、foo()を使用して、forループをトリガーすることができます。

3
ZenOfPython

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
1
Noah