__next__ 在生成器和迭代器中,什么是方法包装器?

问题描述

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

I was reading about generator and iterators and the role of __next__() .

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

'__next__' in dir(mylist),为假

当我深入研究它时,

'__next__' in dir (mylist.__iter__()) 为真

  1. 为什么 __next__ 仅可用于列出,而仅可用于 __iter__()mygen 而不能用于 mylist.__iter__() 当我们使用 list-comprehension 遍历列表时如何调用 __next__

  1. why is__next__ only available to list but only to __iter__() and mygen but not mylist. How does __iter__() call __next__ when we are stepping thru the list using list-comprehension

尝试手动升级生成器 (+1),我调用了 mygen.__next__().它不存在.它仅作为 mygen.__next__ 存在,称为 method-wrapper.

Trying to manually step (+1) up the generator, I called mygen.__next__(). It doesn't exist. It only exist as mygen.__next__which is called method-wrapper.

什么是方法包装器,它有什么作用?它在这里是如何应用的:在 mygen() 和 __iter__() 中?

what is a method-wrapper and what does it do? How is it applied here: in mygen() and __iter__() ?

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

if __next__ is what both generator and iterator provide (and their sole properties) then what is the difference between generator and iterator?*

答案 3:已解决,如 mod/editor 所述:

Answer to 3: Solved, as noted by mod/editor:

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

更新:生成器和迭代器都有 __next__().我的错.查看日志,不知何故 mygen.__next__() 测试给了我停止迭代异常错误.但我无法再次复制该错误.

UPDATE: both generator and iterator have __next__(). My mistake. Looking at the logs, somehow mygen.__next__() test was giving me stopiteration exception error. But I wasn't able to duplicate that error again.

感谢大家的回答!


解决方案

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

The special methods __iter__ and __next__ are part of the iterator protocol to create iterator types. For this purpose, you have to differentiate between two separate things: Iterables and iterators.

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

Iterables are things that can be iterated, usually, these are some kind of container elements that contain items. Common examples are lists, tuples, or dictionaries.

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

In order to iterate an iterable, you use an iterator. An iterator is the object that helps you iterate through the container. For example, when iterating a list, the iterator essentially keeps track of which index you are currently at.

为了获得一个迭代器,__iter__ 方法在迭代器上被调用.这就像一个工厂方法,它为这个特定的迭代返回一个新的迭代器.定义了 __iter__ 方法的类型,将其转换为可迭代对象.

To get an iterator, the __iter__ method is called on the iterable. This is like a factory method that returns a new iterator for this specific iterable. A type having a __iter__ method defined, turns it into an iterable.

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

The iterator generally needs a single method, __next__, which returns the next item for the iteration. In addition, to make the protocol easier to use, every iterator should also be an iterable, returning itself in the __iter__ method.

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

As a quick example, this would be a possible iterator implementation for a list:

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

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

The list implementation could then simply return ListIterator(self) from the __iter__ method. Of course, the actual implementation for lists is done in C, so this looks a bit different. But the idea is the same.

迭代器在 Python 的各个地方都以不可见的方式使用.例如 for 循环:

Iterators are used invisibly in various places in Python. For example a for loop:

for item in lst:
    print(item)

这与以下内容类似:

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)

所以 for 循环从可迭代对象请求一个迭代器,然后在该可迭代对象上调用 __next__ 直到它遇到 StopIteration 异常.这发生在表面之下也是您希望迭代器也实现 __iter__ 的原因:否则您永远无法循环遍历迭代器.

So the for loop requests an iterator from the iterable object, and then calls __next__ on that iterable until it hits the StopIteration exception. That this happens under the surface is also the reason why you would want iterators to implement the __iter__ as well: Otherwise you could never loop over an iterator.

对于生成器,人们通常所说的实际上是生成器function,即一些具有yield语句的函数定义.一旦你调用了那个生成器函数,你就会得到一个generator.生成器本质上只是一个迭代器,尽管它是一个花哨的迭代器(因为它不仅仅是在容器中移动).作为一个迭代器,它有一个 __next__ 方法来生成"下一个元素,以及一个 __iter__ 方法来返回自己.

As for generators, what people usually refer to is actually a generator function, i.e. some function definition that has yield statements. Once you call that generator function, you get back a generator. A generator is esentially just an iterator, albeit a fancy one (since it does more than move through a container). As an iterator, it has a __next__ method to "generate" the next element, and a __iter__ method to return itself.

一个示例生成器函数如下:

An example generator function would be the following:

def exampleGenerator():
    yield 1
    print('After 1')
    yield 2
    print('After 2')

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

The function body containing a yield statement turns this into a generator function. That means that when you call exampleGenerator() you get back a generator object. Generator objects implement the iterator protocol, so we can call __next__ on it (or use the the next() function as above):

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

请注意,第一个 next() 调用尚未打印任何内容.这是生成器的特殊之处:它们是惰性的,并且只评估从可迭代项中获取下一项所需的值.只有通过第二次 next() 调用,我们才能从函数体中获得第一行打印.我们需要另一个 next() 调用来耗尽可迭代对象(因为没有产生另一个值).

Note that the first next() call did not print anything yet. This is the special thing about generators: They are lazy and only evaluate as much as necessary to get the next item from the iterable. Only with the second next() call, we get the first printed line from the function body. And we need another next() call to exhaust the iterable (since there’s not another value yielded).

但除了那种懒惰之外,生成器就像迭代一样.你甚至会在最后得到一个 StopIteration 异常,它允许生成器(和生成器函数)用作 for 循环源以及可以使用正常"迭代的任何地方.

But apart from that laziness, generators just act like iterables. You even get a StopIteration exception at the end, which allows generators (and generator functions) to be used as for loop sources and wherever "normal" iterables can be used.

生成器及其惰性的最大好处是能够按需生成东西.一个很好的类比是网站上的无休止滚动:您可以在之后(在生成器上调用 next() )之后向下滚动项目,并且每隔一段时间,网站将不得不查询后端检索更多项目供您滚动浏览.理想情况下,这种情况会在您不注意的情况下发生.这正是生成器所做的.它甚至允许这样的事情:

The big benefit of generators and their laziness is the ability to generate stuff on demand. A nice analogy for this is endless scrolling on websites: You can scroll down item after after (calling next() on the generator), and every once in a while, the website will have to query a backend to retrieve more items for you to scroll through. Ideally, this happens without you noticing. And that’s exactly what a generator does. It even allows for things like this:

def counter():
    x = 0
    while True:
        x += 1
        yield x

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

Non-lazy, this would be impossible to compute since this is an infinite loop. But lazily, as a generator, it’s possible to consume this iterative one item after an item. I originally wanted to spare you from implementing this generator as a fully custom iterator type, but in this case, this actually isn’t too difficult, so here it goes:

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

    def __iter__ (self):
        return self

    def __next__ (self):
        self.x += 1
        return self.x

相关文章