生成器和迭代器中的__next__以及什么是方法包装器?

the*_*Dog 6 python iterator generator wrapper python-3.x

我正在阅读关于生成器和迭代器以及它的作用__next__().

'__next__' in dir(mygen).是真的

'__next__' in dir(mylist),是假的

当我深入研究它时,

'__next__' in dir (mylist.__iter__()) 是真的

  1. 为什么__next__只适用于列表中,但只有__iter__()mygen,但不会mylist.当我们使用list-comprehension踩到列表时,如何__iter__()调用__next__

    我打电话试图手动步进(+1)发电机mygen.__next__().它不存在.它只存在于mygen.__next__被称为method-wrapper的地方.

  2. 什么是方法包装器,它做什么?它是如何应用的:inmygen() and __iter__() ?

  3. 如果__next__是生成器和迭代器提供的(以及它们的唯一属性)那么生成器和迭代器之间有什么区别?*

    答案3:解决,如mod /编辑所述:

    Python的生成器和迭代器之间的区别

更新:生成器和迭代器都有__next__().我的错.看着日志,某种程度上mygen.__next__()测试给了我停止异常错误.但我无法再次复制该错误.

谢谢大家回答!

pok*_*oke 25

特殊的方法__iter____next__是迭代器协议创建的一部分迭代器类型.为此,您必须区分两个不同的东西:Iterables迭代器.

Iterables是可以迭代的东西,通常,这些是包含项目的某种容器元素.常见示例是列表,元组或字典.

为了迭代迭代,您使用迭代器.迭代器是帮助您遍历容器的对象.例如,在迭代列表时,迭代器基本上会跟踪您当前所在的索引.

要获取迭代器,__iter__可以在iterable上调用该方法.这就像一个工厂方法,为这个特定的iterable返回一个新的迭代器.__iter__定义了方法的类型将其转换为可迭代的类型.

迭代器通常需要一个方法,__next__它返回迭代的下一个项目.另外,为了使协议更容易使用,每个迭代器也应该是一个可迭代的,在__iter__方法中返回自己.

作为一个简单的例子,这可能是列表的迭代器实现:

class ListIterator:
    def __init__ (self, lst):
        self.lst = lst
        self.idx = 0

    def __iter__ (self):
        return self

    def __next__ (self):
        try:
            item = self.lst[self.idx]
        except IndexError:
            raise StopIteration()
        self.idx += 1
        return item
Run Code Online (Sandbox Code Playgroud)

然后,列表实现可以简单地ListIterator(self)__iter__方法返回.当然,列表的实际实现是在C中完成的,所以这看起来有点不同.但这个想法是一样的.

迭代器在Python的各个地方都被隐藏起来.例如一个for循环:

for item in lst:
    print(item)
Run Code Online (Sandbox Code Playgroud)

这与以下内容相同:

lst_iterator = iter(lst) # this just calls `lst.__iter__()`
while True:
    try:
        item = next(lst_iterator) # lst_iterator.__next__()
    except StopIteration:
        break
    else:
        print(item)
Run Code Online (Sandbox Code Playgroud)

因此for循环从可迭代对象请求迭代器,然后调用__next__该迭代,直到它遇到StopIteration异常.这种情况发生在表面下也是你希望迭代器实现它的原因__iter__:否则你永远不能遍历迭代器.


至于生成器,人们通常所指的实际上是一个生成器函数,即一些具有yield语句的函数定义.一旦调用该生成器函数,就会返回一个生成器.一个生成器实际上只是一个迭代器,虽然它是一个奇特的(因为它不仅仅是通过容器移动).作为迭代器,它有一个__next__"生成"下一个元素的__iter__方法,以及一个返回自身的方法.


示例生成器函数如下:

def exampleGenerator():
    yield 1
    print('After 1')
    yield 2
    print('After 2')
Run Code Online (Sandbox Code Playgroud)

包含yield语句的函数体将其转换为生成器函数.这意味着当你打电话时,你exampleGenerator()会得到一个发电机对象.Generator对象实现迭代器协议,因此我们可以调用__next__它(或使用next()上面的函数):

>>> x = exampleGenerator()
>>> next(x)
1
>>> next(x)
After 1
2
>>> next(x)
After 2
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    next(x)
StopIteration
Run Code Online (Sandbox Code Playgroud)

请注意,第一个next()调用尚未打印任何内容.这是关于生成器的特殊之处:它们是惰性的,只能根据需要进行评估以从迭代中获取下一个项目.只有第二次next()调用,我们从函数体中获取第一个打印的行.我们需要另一个next()调用来消耗迭代(因为没有产生另一个值).

但除了那种懒惰之外,发电机就像可迭代一样.您甚至可以StopIteration在结尾处获得异常,这允许将生成器(和生成器函数)用作for循环源以及可以使用"正常"迭代的任何位置.

发电机及其懒惰的巨大好处是能够按需生成物料.一个很好的类比是在网站上无限滚动:您可以在之后向下滚动项目(调用next()生成器),并且每隔一段时间,网站将不得不查询后端以检索更多项目供您滚动.理想情况下,这种情况在您没有注意到的情 这正是发电机的作用.它甚至允许这样的事情:

def counter():
    x = 0
    while True:
        x += 1
        yield x
Run Code Online (Sandbox Code Playgroud)

非懒惰,这是不可能计算的,因为这是一个无限循环.但懒洋洋地说,作为一个生成器,可以在一个项目之后使用这个迭代的一个项目.我原本想让你免于将这个生成器实现为一个完全自定义的迭代器类型,但在这种情况下,这实际上并不太难,所以这里是:

class CounterGenerator:
    def __init__ (self):
        self.x = 0

    def __iter__ (self):
        return self

    def __next__ (self):
        self.x += 1
        return self.x
Run Code Online (Sandbox Code Playgroud)

  • 哇!太感谢了。那是相当广泛了!我现在正在读书。 (3认同)

Jim*_*ard 5

为什么__next__只能列出,但只能列出__iter__()mygen但不能mylist。当我们使用列表理解逐步浏览列表时如何调用__iter__()__next__

因为列表有一个单独的对象返回来iter处理迭代,所以该对象__iter__被连续调用。

所以,对于列表:

iter(l) is l # False, returns <list-iterator object at..>
Run Code Online (Sandbox Code Playgroud)

而对于发电机:

iter(g) is g # True, its the same object
Run Code Online (Sandbox Code Playgroud)

在循环构造中,iter首先会在要循环的目标对象上调用 is 。iter调用__iter__并预计返回一个迭代器;它__next__会被调用,直到没有更多元素可用为止。

什么是方法包装器以及它的作用是什么?它在这里是如何应用的:inmygen()__iter__()

如果我没记错的话,方法包装器是在C. 这就是这两个iter(list).__iter__list是在 中实现的对象C)和gen.__iter__(这里不确定,但生成器也可能是)。

如果__next__生成器和迭代器都提供(以及它们唯一的属性),那么生成器和迭代器之间有什么区别?

生成器是一个迭代器,正如 提供的迭代器一样iter(l)。它是一个迭代器,因为它提供了一个__next__方法(通常,当在 for 循环中使用时,它能够提供值直到耗尽)。