我正在阅读关于生成器和迭代器以及__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__
,称为method-wrapper。
什么是方法包装器,它有什么作用?它是如何应用的:mygen() and __iter__() ?
如果__next__
是生成器和迭代器提供的(以及它们的唯一属性)那么生成器和迭代器之间有什么区别?*
回答3:解决,如mod /编辑所述:
更新:生成器和迭代器都有__next__()
。我的错。查看日志,不知怎的mygen.__next__()
测试给了我停止异常错误。但是我无法再次复制该错误。
感谢大家的回答!
答案 0 :(得分:22)
特殊方法__iter__
和__next__
是创建iterator types的迭代器协议的一部分。为此,您必须区分两个不同的东西: Iterables 和迭代器。
Iterables 是可以迭代的东西,通常,这些是包含项目的某种容器元素。常见的例子是列表,元组或字典。
为了迭代一个iterable,你使用 iterator 。迭代器是帮助您遍历容器的对象。例如,在迭代列表时,迭代器基本上会跟踪您当前所在的索引。
要获取迭代器,可在iterable上调用__iter__
方法。这就像一个工厂方法,为这个特定的iterable返回一个新的迭代器。定义了__iter__
方法的类型将其转换为可迭代的方法。
迭代器通常需要一个方法__next__
,它返回迭代的 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循环从iterable对象请求迭代器,然后在该iterable上调用__next__
,直到它遇到StopIteration
异常。这种情况发生在表面下也是你希望迭代器实现__iter__
的原因:否则你永远不能遍历迭代器。
对于生成器,人们通常所指的实际上是一个生成器 function ,即一些具有yield
语句的函数定义。一旦调用该生成器函数,就会返回生成器。一个生成器实际上只是一个迭代器,虽然它很奇特(因为它不仅仅是通过容器移动)。作为迭代器,它有一个__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
答案 1 :(得分:2)
为什么
__next__
仅可用于列出,但仅限于__iter__()
和mygen
,但不是mylist
。当我们使用list-comprehension单步执行列表时,__iter__()
如何调用__next__
。
因为列表具有从iter
返回以处理迭代的单独对象,所以连续调用此对象__iter__
。
所以,对于列表:
iter(l) is l # False, returns <list-iterator object at..>
虽然,对于发电机:
iter(g) is g # True, its the same object
在循环结构中,首先要在要循环的目标对象上调用iter
。 iter
调用__iter__
并返回迭代器;调用其__next__
,直到没有更多元素可用。
什么是方法包装器,它有什么作用?它是如何应用的:
mygen()
和__iter__()
?
如果我没记错的话,方法包装器是C
中实现的方法。这些iter(list).__iter__
(list
是C
中实现的对象)和gen.__iter__
(这里不确定,但生成器可能也是如此)的原因是什么。
如果
__next__
是生成器和迭代器都提供的(以及它们的唯一属性)那么生成器和迭代器之间的区别是什么?
生成器是迭代器,iter(l)
提供的迭代器也是如此。它是一个迭代器,因为它提供了一个__next__
方法(通常,当它在for循环中使用时,它能够提供值直到耗尽)。
答案 2 :(得分:1)
__next__
或__iter__
时, next(some_gen)
和iter(some_sequence)
是方法包装器。 next(some_gen)
与some_gen.__next__()
因此,如果我执行mygen = iter(mylist)
,那么mygen
mylist
被实现为生成器对象,并且具有__next__
方法描述符。列表本身没有这种方法,因为它们不是生成器。