python迭代器、生成器和介于两者之间

Posted

技术标签:

【中文标题】python迭代器、生成器和介于两者之间【英文标题】:python iterators, generators and in between 【发布时间】:2020-06-14 18:44:21 【问题描述】:

所以我得到了用于惰性求值和生成器表达式的生成器函数,也就是生成器理解作为它的语法糖等价物。

我了解类似的课程

class Itertest1:
    def __init__(self):
        self.count = 0
        self.max_repeats = 100

    def __iter__(self):
        print("in __inter__()")
        return self

    def __next__(self):
        if self.count >= self.max_repeats:
            raise StopIteration
        self.count += 1
        print(self.count)
        return self.count

作为实现迭代器接口的一种方式,即 iter() 和 next() 在同一个类中。

但那是什么

class Itertest2:
    def __init__(self):
        self.data = list(range(100))

    def __iter__(self):
        print("in __inter__()")
        for i, dp in enumerate(self.data):
            print("idx:", i)
            yield dp

iter成员函数中使用yield语句?

我还注意到在调用 iter 成员函数时

it = Itertest2().__iter__()
batch = it.__next__()

仅在第一次调用 next() 时才会执行打印语句。 这是由于产量和迭代的这种奇怪的混合吗?我认为这很违反直觉......

【问题讨论】:

在生成器中,值是延迟计算的值仅在需要时计算。 (1) 你应该以不同的方式命名这两个类,至少 Itertest1 和 Itertest2。 (2) Itertest2 是一个可迭代对象,它在其__iter__ 方法中创建新的独立迭代器。生成器函数返回这样一个迭代器。 Itertest1 是一个迭代器,按照惯例,它在__iter__ 中返回自身。例如。 Java 更清晰地区分可迭代和迭代器,但不太舒服。 谢谢,但为什么在“__inter__()”中只在下一次调用而不是调用 __iter__() 之后才打印? @CD86 因为对__iter__ 的调用只会返回您的生成器。事情是,yield 旨在简化编写迭代器的过程(除其他外),幕后发生了很多事情。因此,您没有在定义 both __iter__ __next__ 时所做的控制级别;你把它们挤在一起并用yield粘在一起。 __iter__ itself 是一个生成器函数,所以每次调用Itertest2.__iter__ 都会返回一个独立的迭代器,这与Itertest1 不同,Itertest1 的实例本身带有迭代状态。 【参考方案1】:

可以使用单独的迭代器类编写与 Itertest2 等效的内容。

class Itertest3:
    def __init__(self):
        self.data = list(range(100))

    def __iter__(self):
        return Itertest3Iterator(self.data)


class Itertest3Iterator:
    def __init__(self, data):
        self.data = enumerate(data)

    def __iter__(self):
        return self

    def __next__(self):
        print("in __inter__()")
        i, dp = next(self.state)  # Let StopIteration exception propagate
        print("idx:", i)
        return dp

将此与Itertest1 进行比较,Itertest1 的实例本身在其中携带了迭代的状态。对Itertest1.__iter__ 的每次调用都返回相同的对象(Itertest1 的实例),因此它们无法独立地迭代数据。

请注意,我将 print("in __iter__()") 放入 __next__,而不是 __iter__。正如您所观察到的,在第一次调用__next__ 之前,生成器函数中实际上没有执行。生成器函数本身创建一个生成器;它实际上并没有开始执行其中的代码。

【讨论】:

【参考方案2】:

在任何函数中的任何位置使用yield 语句都会将函数代码包装在(本机)生成器对象中,并将函数替换为为您提供所述生成器对象的存根。

所以,在这里,调用__iter__ 会给你一个匿名的生成器对象,它会执行你想要的代码。

__next__ 的主要用例是提供一种无需依赖(本机)生成器即可编写迭代器的方法。

__iter__ 的用例是区分对象和对所述对象的迭代状态。考虑类似的代码

c = some_iterable()
for a in c:
    for b in c:
        # do something with a and b

您不希望两个交错的迭代相互干扰。这就是为什么这样的循环会脱糖到像

c = some_iterable()
_iter1 = iter(c)
try:
    while True:
        a = next(_iter1)
        _iter2 = iter(c)
        try:
            while True:
                b = next(_iter2)
                # do something with a and b
        except StopIteration:
            pass
 except StopIteration:
     pass

通常,自定义迭代器实现一个返回 self 的存根 __iter__,因此 iter(iter(x)) 等效于 iter(x)。这在编写迭代器包装器时很重要。

【讨论】:

明白了。不过,在第一次调用相当隐蔽的 next 方法之前,不喜欢在所述成员函数中隐式延迟代码。似乎很奇怪。因此,iter 成员函数的主要用例是能够让类与纯生成器函数相比保持状态,同时比具有 iter 和 next 方法?

以上是关于python迭代器、生成器和介于两者之间的主要内容,如果未能解决你的问题,请参考以下文章

流畅的python14 章可迭代的对象迭代器 和生成器

迭代器与生成器 全面解析

Python迭代对象迭代器生成器

python-19-迭代器是个什么东西?

完全理解Python迭代对象迭代器生成器

完全理解Python迭代对象迭代器生成器