2011-07-23 8 views
0

収量の仕組みを理解しようとしていて、これを読んだ後にtext私はそれがかなり理解できると信じていました。私はちょうどこのコードが動作することがわかってきたので、私はまだ歩留まりと__iter__との間の接続何を理解していないがyieldがイテレータを返すのはなぜですか?

class Vect(): 
    def __init__(self, *args): 
     self.__a = list(args) 
    def print_(self): 
     print self.__a 
    def __iter__(self): 
     yield self.__a 

asd = Vect(1,2,3,4,5) 
for foo in asd: 
    print foo 

私は返すジェネレータ(機能を持っている場合と思いましたその時の単一の引数ですが、終わりに達するまでできるだけ多くの引数を返します)yieldは次のようになります: "OK、この引数を返しますが、別のものを返すことができます。しかし、私の例では、ジェネレータはありません。リストを返し、何らかの形でリストのイテレータにアクセスします。私は何が起こっているのか全く分かりません。

+1

yield文を使用するメソッドは、イテレータです。返される値は、イテレータによってアクセスされる値です。 – robert

+0

ところで、あなたは、Pythonがイテレータによって最後に返された要素をどのように記憶しているかを知っていますか? – Michal

+0

"私の例ではジェネレータがありません" - yieldステートメントを含む関数はジェネレータです。あなたは何が必要なのですか?ジェネレータ**の式**?それは1つの種類の発電機に過ぎません。 –

答えて

9

yieldリターンそのオブジェクトがシーケンス、ジェネレータまたはその他のイテレータであっても、渡されるオブジェクトは何でもしたがって:(それはfor x in obj構築物の一部である場合のように)オブジェクトのコンテンツ反復子が必要とされる場合

>>> def g(): 
...  yield [1,2,3] 
...  yield 1 
...  yield 2 
...  yield 3 
... 
>>> gen = g() 
>>> gen.next() 
[1, 2, 3] 
>>> gen.next() 
1 
>>> gen.next() 
2 
>>> gen.next() 
3 
>>> gen.next() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
StopIteration 
>>> 

__iter__は、オブジェクト上で呼び出されます。 yieldを使用すると(ジェネレータはイテレータなので)ジェネレータを作成できますが、この例では必要ありません。以下は同様に動作します:

def __iter__(self): 
    return iter(self.__a) 

あなたがyieldを使用したい、とあなたはVectオブジェクトがベクトルの内容を移動するためのイテレータをしたい場合は、収率に持っている各値を別々

def __iter__(self): 
    for i in self.__a: 
     yield i 

yield__iter__は、発電機を返すことを意味し、ジェネレータオブジェクトにnext()を呼び出すと、それは__aを反復処理として、それは最後の、オフに左の点で機能を再開します。

======= Pythonはどこにある発電機の実行中に追跡する方法に関する追加質問に対して

が、私はそれがf_lastiを使用すると信じて(==「最後の命令」)のジェネレータのgi_frame属性(ジェネレータは、通常の関数とは異なり、それらのジェネレータの周りに実行フレームを持ちます)。ここでその周りに工具のビット値がどのように変化するかを示します:

>>> import dis 
>>> def g(): 
...  yield 1 
...  for i in range(10): 
...    yield i*2 
... 
>>> gen = g() 
>>> dis.dis(gen.gi_code) 
    2   0 LOAD_CONST    1 (1) 
       3 YIELD_VALUE   
       4 POP_TOP    

    3   5 SETUP_LOOP    29 (to 37) 
       8 LOAD_GLOBAL    0 (range) 
       11 LOAD_CONST    2 (10) 
       14 CALL_FUNCTION   1 
       17 GET_ITER    
     >> 18 FOR_ITER    15 (to 36) 
       21 STORE_FAST    0 (i) 

    4   24 LOAD_FAST    0 (i) 
       27 LOAD_CONST    3 (2) 
       30 BINARY_MULTIPLY  
       31 YIELD_VALUE   
       32 POP_TOP    
       33 JUMP_ABSOLUTE   18 
     >> 36 POP_BLOCK   
     >> 37 LOAD_CONST    0 (None) 
       40 RETURN_VALUE   
>>> gen.gi_frame.f_lasti ## -1 because we haven't started yet 
-1 
>>> gen.next() 
1 
>>> gen.gi_frame.f_lasti 
3 
>>> gen.gi_frame.f_locals 
{} 
>>> gen.next() 
0 
>>> gen.gi_frame.f_lasti , gen.gi_frame.f_locals 
(31, {'i': 0}) 
>>> gen.next() 
2 
>>> gen.gi_frame.f_lasti , gen.gi_frame.f_locals 
(31, {'i': 1}) 
>>> 

注f_lasti値が最後の収率が上であったことをことを逆アセンブルコードに番号の行に対応する方法:ときに発生その時点から再開し再入力されます。

+0

あなたの記事のおかげで、私は歩留まりの仕組みを完全に理解することができました。ありがとう。 – Michal

1

yieldは、あなたが理解した通りに正確に機能します。あなたのケースではリストである1つのオブジェクトを返します。

多分これはそれがより明確になります - にあなたのコードを変更します - イテレータが一つの要素が含まれています - リスト[1,2,3,4だけ一回の反復がある見ることができるように

i = 1 
for foo in asd: 
    print i 
    i += 1 
    print foo 

、5]。

1

あなたの印刷をチェックする場合は、 ITER全体としてのリストを返して表示されます、あなたは、印刷時に一度だけプリント

チェックこのアウトそれを狛犬:

for index, foo in enumerate(asd): 
    print index, foo 
+0

ええ、それはモニターのこの側でとても遅いので、私はあまり注意を払っていませんでした。そして、Pythonがエラーを出さなかったとき、私はそれがどのように動作するかを仮定しました。 – Michal

関連する問題