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迭代器、生成器和介于两者之间的主要内容,如果未能解决你的问题,请参考以下文章