2016-07-29 7 views
6

itをpythonの反復可能な要素としましょう。 it以上のループ内でitが変更された場合はどうなりますか?それとももっと簡単です:この作品のようなものはいつですか?ループ中に繰り返し可能な変数を変更する

it = range(6) 
for i in it: 
    it.remove(i+1) 
    print i 

0,2,4が印刷されます(ループが3回実行されることを示します)。一方

を出力する

it = range(6) 
for i in it: 
    it = it[:-2] 
    print it 

リードん

[0,1,2,3] 
[0,1] 
[] 
[] 
[] 
[], 

ループを示すが6回実行されます。私は、インプレース操作や可変スコープと関係があると思いますが、100%確実に私の頭を包み込むことはできません。

Clearification:動作しません

一例として、:

it = range(6) 
for i in it: 
    it = it.remove(i+1) 
    print it 

がスローされる(NoneTypeは何の属性 '削除' を持っていない)印刷されている 'なし' とエラーにつながります。

+0

2番目の例では、** iterableを変更していない**(iterableでなく、どちらもiterableではありません)。 –

+0

参考:http://stackoverflow.com/questions/9884132/what-exactly-are-pythons-iterator-iterable-and-iteration-protocols?noredirect=1&lq=1 – ecatmur

答えて

6

あなたはlistを反復すると、あなたが実際にlistにバインドされたlistiteratorオブジェクトを返しますlist.__iter__()を呼び出し、その後、実際にこのlistiteratorを反復。技術的には、この:

itt = [1, 2, 3] 
for i in itt: 
    print i 

はのための一種のシンタックスシュガー実際にある:

itt = [1, 2, 3] 
iterator = iter(itt) 
while True: 
    try: 
     i = it.next() 
    except StopIteration: 
     break 
    print i 

だから、この時点では - ループ内で - 、再バインドittは影響しません、それは自身の参照をだ保つlistiterator(しかし、を突然変異させるittは明らかにそれに影響を与えます(両方の参照が同じリストを指しているため)。あなたがのitオブジェクト(内部状態を変更している最初のループでは

# creates a `list` and binds it to name "a" 
a = [1, 2, 3] 
# get the object bound to name "a" and binds it to name "b" too. 
# at this point "a" and "b" both refer to the same `list` instance 
b = a 
print id(a), id(b) 
print a is b 
# so if we mutate "a" - actually "mutate the object bound to name 'a'" - 
# we can see the effect using any name refering to this object: 
a.append(42) 
print b 

# now we rebind "a" - make it refer to another object 
a = ["a", "b", "c"] 
# at this point, "b" still refer to the first list, and 
# "a" refers to the new ["a", "b", "c"] list 
print id(a), id(b) 
print a is b 
# and of course if we now mutate "a", it won't reflect on "b" 
a.pop() 
print a 
print b 
+0

もう少し拡張できますか?他のオブジェクトがありますか?これは同じケースですか? – Dschoni

+1

@Dschoniは、「同じケース」の意味に依存します... wrt /「変異対vs再バインド」部分は、Pythonの動作方法です。「変数」はオブジェクトを参照する名前だけの「メモリーバケット」ではありません。イテレータプロトコルについて詳しく知りたい場合は、PythonのFineManualで完全に文書化されていますが、ここで紹介やその他の参考文献を得ることができます:http://stackoverflow.com/questions/9884132/what-exactly-are-pythons-iterator -iterable-iteration-protocols –

4

IOW、それは再バインドと変異との同じ古い違いだ...あなたはforループせずに同じ動作を取得したいですただし、2番目のループでは、itを別のオブジェクトに再割り当てし、初期オブジェクトは変更しません。

はのは、生成されたバイトコードを見てみましょう:あなたが見ることができるように

In [2]: def f1(): 
    ...:  it = range(6) 
    ...:  for i in it: 
    ...:   it.remove(i + 1) 
    ...:   print i 
    ...:   

In [3]: def f2(): 
    ...:  it = range(6) 
    ...:  for i in it: 
    ...:   it = it[:-2] 
    ...:   print it 
    ...:   

In [4]: import dis 

In [5]: dis.dis(f1) 
    2   0 LOAD_GLOBAL    0 (range) 
       3 LOAD_CONST    1 (6) 
       6 CALL_FUNCTION   1 
       9 STORE_FAST    0 (it) 

    3   12 SETUP_LOOP    36 (to 51) 
      15 LOAD_FAST    0 (it) 
      18 GET_ITER    
     >> 19 FOR_ITER    28 (to 50) 
      22 STORE_FAST    1 (i) 

    4   25 LOAD_FAST    0 (it) 
      28 LOAD_ATTR    1 (remove) 
      31 LOAD_FAST    1 (i) 
      34 LOAD_CONST    2 (1) 
      37 BINARY_ADD   
      38 CALL_FUNCTION   1 
      41 POP_TOP    

    5   42 LOAD_FAST    1 (i) 
      45 PRINT_ITEM   
      46 PRINT_NEWLINE  
      47 JUMP_ABSOLUTE   19 
     >> 50 POP_BLOCK   
     >> 51 LOAD_CONST    0 (None) 
      54 RETURN_VALUE   

In [6]: dis.dis(f2) 
    2   0 LOAD_GLOBAL    0 (range) 
       3 LOAD_CONST    1 (6) 
       6 CALL_FUNCTION   1 
       9 STORE_FAST    0 (it) 

    3   12 SETUP_LOOP    29 (to 44) 
      15 LOAD_FAST    0 (it) 
      18 GET_ITER    
     >> 19 FOR_ITER    21 (to 43) 
      22 STORE_FAST    1 (i) 

    4   25 LOAD_FAST    0 (it) 
      28 LOAD_CONST    2 (-2) 
      31 SLICE+2    
      32 STORE_FAST    0 (it) 

    5   35 LOAD_FAST    0 (it) 
      38 PRINT_ITEM   
      39 PRINT_NEWLINE  
      40 JUMP_ABSOLUTE   19 
     >> 43 POP_BLOCK   
     >> 44 LOAD_CONST    0 (None) 

は、for文はitの反復可能(GET_ITER命令、iter(it))で動作します。したがって、it変数を再割り当てしてもループ反復には影響しません。ループの最初、が作成されイテレータで

for i in it: pass 

+0

あなたは私が決してそれを見たことがないので、あなたは私のupvoteを 'dis'のために持っています。私は自分で自分の明確化事件を調べるつもりです。 – Dschoni

+0

だからこそ私はPythonを好きです - それは驚くべきことをたくさん提供します – soon

4

まず、あなたがforループの簡単なを実行したときのように、ボンネットの下に何が起こるかを理解することが不可欠です。その反復子はiter(it)への暗黙の呼び出しの結果です。これは、上記のループでitという名前の変数が参照されている時のみです。残りの参照は、イテレータでnextが呼び出されたときに発生しますが、イテレータが参照を保持するオブジェクトを使用します。オブジェクト名ではitがバインドされていません。

2番目の例でこれはどういう意味ですか?

2番目の例では、リストを変更せずに新しいリストを作成し、変数itをバインドすることに注意してください。

これは、イテレータが元のリストを参照し続けることを意味しますが、これは変更されません。

最初の例では、元のリストを変更して、next(iterator)にこれらの変更が反映されます。

関連する問題