问题描述
特殊方法__iter__
和__next__
是创建迭代器类型的迭代器协议的一部分。为此,您必须区分两个独立的事物: 和 。
Iterables 有些事情可以重复,通常情况下,这些都是某种包含物品的容器元素。常见的例子是列表,元组或字典。
为了迭代可迭代对象,您可以使用 iterator 。迭代器是帮助您遍历容器的对象。例如,当迭代列表时,迭代器本质上会跟踪您当前所处的索引。
为了获得一个迭代器,在迭代器__iter__
上调用该方法。这就像一个工厂方法,将为此特定可迭代方法返回一个新的迭代器。具有__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
然后,列表实现可以简单地ListIterator(self)
从__iter__
方法中返回。当然,列表的实际实现是在C中完成的,因此看起来有些不同。但是想法是一样的。
迭代器在Python中的各个地方都被不可见地使用。例如一个for
循环:
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__
:否则,您将永远无法遍历迭代器。
对于生成器,人们通常所指的实际上是生成器 函数 ,即具有yield
语句的某些函数定义。调用该生成器函数后,您将返回一个 generator
。生成器本质上只是一个迭代器,尽管它是花哨的(因为它比在容器中移动要多)。作为迭代器,它具有__next__
“生成”下一个元素的__iter__
方法和返回自身的方法。
def exampleGenerator():
yield 1
print('After 1')
yield 2
print('After 2')
包含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
请注意,第一个next()
呼叫尚未打印任何内容。这是关于生成器的特殊之处:它们是惰性的,并且仅评估必要的内容以从迭代器中获取下一项。只有第二次next()
调用,我们才能从函数体中获得第一条打印行。而且我们需要另一个next()
调用来耗尽可迭代对象(因为没有其他值可产生)。
但是除了这种懒惰之外,生成器的行为就像可迭代的一样。stopiteration
最后,您甚至会遇到一个异常,该异常允许将生成器(和生成器函数)用作for
循环源,并在任何可以使用“常规”可迭代项的地方使用。
发电机及其懒惰的最大好处是可以 按需
生产东西。一个很好的类比是在网站上无休止地滚动:您可以在之后(调用next()
生成器)之后向下滚动项目,并且每隔一段时间,网站将不得不查询后端以检索更多项目以供您滚动浏览。理想情况下,这种情况不会引起您的注意。而这正是发电机的作用。它甚至允许这样的事情:
def counter():
x = 0
while True:
x += 1
yield x
非延迟的,由于这是一个无限循环,因此无法计算。但是懒惰地,作为生成器,有可能在一个项目之后消耗一个迭代的项目。我本来想让您不必将此生成器实现为完全自定义的迭代器类型,但是在这种情况下,这实际上并不是太困难,所以就来了:
class CounterGenerator:
def __init__ (self):
self.x = 0
def __iter__ (self):
return self
def __next__ (self):
self.x += 1
return self.x
解决方法
我正在阅读有关生成器和迭代器以及的作用的信息__next__()
。
'__next__' in dir(mygen)
。是真的
'__next__' in dir(mylist)
,是错误的
当我深入研究时,
'__next__' in dir (mylist.__iter__())
是真的
- 为什么
__next__
只对列表中,但只有__iter__()
和mygen
,但不会mylist
。当我们使用list-comprehension遍历列表时如何__iter__()
调用__next__
尝试手动使(+1)生成器步进,我致电mygen.__next__()
。它不存在。它只存在于mygen.__next__
所谓的方法包装器中。
-
什么是方法包装器?它有什么作用?在这里如何应用:
mygen() and __iter__() ?
-
如果
__next__
Generator和Iterator都提供什么(及其唯一属性),那么Generator和Iterator之间有什么区别?*
答案3:已解决,如mod / editor所述:
更新:生成器和迭代器都具有__next__()
。我的错。查看日志,以某种方式mygen.__next__()
测试给了我stopiteration异常错误。但是我无法再次复制该错误。
谢谢大家的回答!