2011-07-23 146 views
0

我试图理解产量是如何工作的,而且在我读完text之后,我相信这是可以理解的。为什么yield会返回迭代器?

不过我还是不明白,因为我刚刚发现这个代码的工作什么是产量和__iter__之间的连接:

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的工作原理如下:“好吧,让我们返回这个参数,但也许我们仍然可以返回另一个参数”。然而在我的例子中,我没有任何生成器,yield“返回”列表并以某种方式访问​​列表的迭代器。我完全不知道发生了什么事。

+1

带有yield语句的方法是一个迭代器。产生的值是迭代器访问的值。 – robert

+0

顺便问一下,您是否知道Python如何记住哪个是迭代器产生的最后一个元素? – Michal

+0

“但是在我的例子中,我没有任何生成器” - 一个包含yield语句的函数是一个生成器。你期望什么需要?一个generator **表达式**?这只是一种发电机。 –

回答

9

yield回报无论对象传递给它,即使该对象是一个序列或一个生成器或其他迭代器。因此:

>>> 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__当需要在对象的内容的迭代器(如在一当它是一个for x in obj构建体的一部分)被称为一个对象上。你可以使用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的工作原理与您所了解的完全相同 - 它返回一个对象,在您的情况下它是一个列表。

也许这将使其更清晰 - 你的代码更改为:

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

正如你可以看到只有一个迭代 - 迭代器包含一个元素 - 列表[1,2,3,4 ,5]。

1

,如果您检查您的打印,你会看到ITER被返回列表作为一个整体,当您打印foo的只打印一次

检查了这一点:

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

是的,在监视器的这一边这么晚了,我没有太在意,当Python没有给出错误时,我认为它是如何工作的。 – Michal

相关问题