为什么yield会返回一个迭代器?

我试图理解产量是如何工作的,而且在阅读了这篇文章之后,我相信这是可以理解的。

然而,我仍然不明白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“返回”列表并以某种方式访问​​列表的迭代器。 我完全不知道发生了什么事。


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

当需要对象内容的迭代器时(如for x in obj构造中,它是for x in obj一部分时),将在对象上调用__iter__ 。 您可以使用yield来创建一个生成器(因为生成器是迭代器),但在本示例中,您不需要。 以下内容也适用于:

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

如果您想使用yield ,并且您希望Vect对象的迭代器遍历Vect的内容,则必须分别生成每个值:

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

yield意味着__iter__将返回一个生成器,并且在生成器对象上调用next()将在它最后一次离开的位置恢复该函数,因为它在__a迭代。

=======

为了回应关于Python如何跟踪生成器执行过程中的附加问题,我相信它使用了生成器的gi_frame属性的f_lasti(==“last instruction”)(与普通函数不同,Generators携带与他们执行的框架)。 这里有一些工具可以显示值的变化:

>>> 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值如何对应于反汇编代码中最后一个产量所在的编号行:当重新输入生成器时,它将从该点重新开始。


如果你检查你的打印,你会看到iter整个返回列表,当你打印foo时它只打印一次

看一下这个:

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

产量完全按照你理解的方式工作 - 它返回一个对象,在你的情况下它是一个列表。

也许这会让它更清晰 - 将你的代码改为:

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

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

链接地址: http://www.djcxy.com/p/53771.html

上一篇: Why yield returns an iterator?

下一篇: Editing dictionary values in a foreach loop