为什么 yield from inside __next__() return 生成器对象?

Why does a yield from inside __next__() return generator object?

我在 class 的 __next__() 函数中使用 yield 来 return 下一个值。但是它不会 return 下一个值,它 return 是生成器对象。

我正在努力更好地理解迭代器和 yield。我可能做错了。

看看。

class MyString:
    def __init__(self,s):
        self.s=s

    def __iter__(self):
        return self

    def __next__(self):
        for i in range(len(self.s)):
            yield(self.s[i])

r=MyString("abc")
i=iter(r)
print(next(i))

这个returns:

generator object __next__ at 0x032C05A0

next 在这种情况下几乎只是调用 __next__()。在你的对象上调用 __next__ 将启动生成器并 return 它(此时没有魔法)。


在这种情况下,您 可能 完全不定义 __next__ 就可以逃脱:

class MyString:
    def __init__(self,s):
        self.s=s

    def __iter__(self):
        for i in range(len(self.s)):
            yield(self.s[i])
        # Or...
        # for item in self.s:
        #     yield item

如果你想使用 __iter____next__(定义一个 iterator rather than simply making an iterable),你可能想做这样的事情:

class MyString:
    def __init__(self,s):
        self.s = s
        self._ix = None

    def __iter__(self):
        return self

    def __next__(self):
        if self._ix is None:
            self._ix = 0

        try:
            item = self.s[self._ix]
        except IndexError:
            # Possibly reset `self._ix`?
            raise StopIteration
        self._ix += 1
        return item

让我们来看看__next__方法的目的。来自 the docs:

iterator.__next__()

Return the next item from the container. If there are no further items, raise the StopIteration exception.

现在让我们看看 yield 语句的作用。 the docs 的另一段摘录:

Using a yield expression in a function’s body causes that function to be a generator

When a generator function is called, it returns an iterator known as a generator.

现在比较 __next__yield__next__ return 容器中的下一个项目 。但是包含 yield 关键字的函数 return 是一个迭代器 。因此,在 __next__ 方法中使用 yield 会产生一个产生迭代器的迭代器。


如果您想使用 yield 使您的 class 可迭代,请在 __iter__ 方法中执行:

class MyString:
    def __init__(self, s):
        self.s = s

    def __iter__(self):
        for s in self.s:
            yield s

__iter__ 方法应该 return 一个迭代器 - 而 yield 关键字使它做到这一点。


为了完整起见,下面是如何使用 __next__ 方法实现迭代器。您必须跟踪迭代的状态,以及 return 相应的值。最简单的解决方案可能是每次调用 __next__ 时增加一个索引:

class MyString:
    def __init__(self,s):
        self.s = s
        self.index = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1

        if self.index >= len(self.s):
            raise StopIteration

        return self.s[self.index]

据我所知,生成器函数只是 classes 和 next 函数的语法糖。示例:

>>> def f():
    i = 0
    while True:
        i += 1
        yield i


>>> x = f()
>>> x
<generator object f at 0x0000000000659938>
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> class g(object):
    def __init__(self):
        self.i = 0

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


>>> y = g()
>>> y
<__main__.g object at 0x000000000345D908>
>>> next(y)
1
>>> next(y)
2
>>> next(y)
3

事实上,我来这里是想看看是否有任何显着差异。有的话请喊一声。

所以,要回答这个问题,您拥有的是一个 class 和 __next__ 方法,returns 一个对象也有一个 __next__ 方法。因此,最简单的做法是用 return 替换 yield 并跟踪你走了多远,并记住在到达数组末尾时引发 StopIteration 。所以像:

class MyString:
    def __init__(self,s):
        self.s=s
        self._i = -1

    def __iter__(self):
        return self

    def __next__(self):
        self._i += 1
        if self._i >= len(self.s):
            raise StopIteration
        return self.s[self._i]

这可能是实现我认为您正在寻找的东西的最简单方法。