__next__ 在生成器和迭代器中以及什么是方法包装器?
__next__ in generators and iterators and what is a method-wrapper?
我正在阅读有关生成器和迭代器以及 __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__
是生成器和迭代器都提供的(以及它们的唯一属性)那么生成器和迭代器之间有什么区别?*
对 3 的回答:已解决,如 mod/editor 所述:
Difference between Python's Generators and Iterators
更新:生成器和迭代器都有 __next__()
。我的错。查看日志,不知何故 mygen.__next__()
测试给了我停止迭代异常错误。但我无法再次复制该错误。
谢谢大家的回答!
特殊方法 __iter__
和 __next__
是创建 iterator types 的迭代器协议的一部分。为此,您必须区分两个不同的事物:Iterables 和 iterators.
Iterables 是可以迭代的东西,通常,这些是某种包含项目的容器元素。常见的例子是列表、元组或字典。
为了迭代可迭代对象,您使用迭代器。迭代器是帮助您遍历容器的对象。例如,在迭代列表时,迭代器本质上会跟踪您当前所在的索引。
为了得到一个迭代器,__iter__
方法在可迭代对象上被调用。这就像一个工厂方法,return 是这个特定可迭代对象的新迭代器。定义了 __iter__
方法的类型,将其转换为可迭代对象。
迭代器通常需要一个方法,__next__
,return是迭代的下一个项目。此外,为了使协议更易于使用,每个迭代器也应该是可迭代的,return在__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
列表实现可以简单地从 __iter__
方法 return ListIterator(self)
。当然,列表的实际实现是用 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__
方法来 return 本身。
生成器函数示例如下:
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()
调用尚未打印任何内容。这是生成器的特殊之处:它们是惰性的,只计算必要的次数以从 iterable 中获取下一个项目。只有在第二次 next()
调用时,我们才能从函数体中获得第一行打印。我们需要另一个 next()
调用来耗尽 iterable(因为没有产生另一个值)。
但是除了懒惰之外,生成器就像可迭代对象一样。你甚至在最后得到一个 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__
和 __iter__
是当您执行 next(some_gen)
或 iter(some_sequence)
时的方法包装器。 next(some_gen)
等同于 some_gen.__next__()
所以如果我做 mygen = iter(mylist)
那么 mygen
是 mylist
作为生成器对象实现的并且有一个 __next__
方法描述符。列表本身没有这个方法,因为它们不是生成器。
生成器是迭代器。查看 difference between generators and iterators
Why is __next__
only available to list but only to __iter__()
and mygen
but not mylist
. How does __iter__()
call __next__
when we are stepping through the list using list-comprehension.
因为列表有一个从 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__
被调用,直到没有更多的元素可用。
What is a method-wrapper and what does it do? How is it applied here: in mygen()
and __iter__()
?
如果我没记错的话,方法包装器是 C
中实现的方法。这就是 iter(list).__iter__
(list
是在 C
中实现的对象)和 gen.__iter__
(这里不确定,但生成器可能也是)。
If __next__
is what both generator and iterator provide (and their sole properties) then what is the difference between generator and iterator?
生成器是迭代器,iter(l)
提供的迭代器也是如此。它是一个迭代器,因为它提供了一个 __next__
方法(通常,当在 for 循环中使用时,它能够提供值直到耗尽)。
我正在阅读有关生成器和迭代器以及 __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__
是生成器和迭代器都提供的(以及它们的唯一属性)那么生成器和迭代器之间有什么区别?*对 3 的回答:已解决,如 mod/editor 所述:
Difference between Python's Generators and Iterators
更新:生成器和迭代器都有 __next__()
。我的错。查看日志,不知何故 mygen.__next__()
测试给了我停止迭代异常错误。但我无法再次复制该错误。
谢谢大家的回答!
特殊方法 __iter__
和 __next__
是创建 iterator types 的迭代器协议的一部分。为此,您必须区分两个不同的事物:Iterables 和 iterators.
Iterables 是可以迭代的东西,通常,这些是某种包含项目的容器元素。常见的例子是列表、元组或字典。
为了迭代可迭代对象,您使用迭代器。迭代器是帮助您遍历容器的对象。例如,在迭代列表时,迭代器本质上会跟踪您当前所在的索引。
为了得到一个迭代器,__iter__
方法在可迭代对象上被调用。这就像一个工厂方法,return 是这个特定可迭代对象的新迭代器。定义了 __iter__
方法的类型,将其转换为可迭代对象。
迭代器通常需要一个方法,__next__
,return是迭代的下一个项目。此外,为了使协议更易于使用,每个迭代器也应该是可迭代的,return在__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
列表实现可以简单地从 __iter__
方法 return ListIterator(self)
。当然,列表的实际实现是用 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__
方法来 return 本身。
生成器函数示例如下:
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()
调用尚未打印任何内容。这是生成器的特殊之处:它们是惰性的,只计算必要的次数以从 iterable 中获取下一个项目。只有在第二次 next()
调用时,我们才能从函数体中获得第一行打印。我们需要另一个 next()
调用来耗尽 iterable(因为没有产生另一个值)。
但是除了懒惰之外,生成器就像可迭代对象一样。你甚至在最后得到一个 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__
和 __iter__
是当您执行 next(some_gen)
或 iter(some_sequence)
时的方法包装器。 next(some_gen)
等同于 some_gen.__next__()
所以如果我做 mygen = iter(mylist)
那么 mygen
是 mylist
作为生成器对象实现的并且有一个 __next__
方法描述符。列表本身没有这个方法,因为它们不是生成器。
生成器是迭代器。查看 difference between generators and iterators
Why is
__next__
only available to list but only to__iter__()
andmygen
but notmylist
. How does__iter__()
call__next__
when we are stepping through the list using list-comprehension.
因为列表有一个从 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__
被调用,直到没有更多的元素可用。
What is a method-wrapper and what does it do? How is it applied here: in
mygen()
and__iter__()
?
如果我没记错的话,方法包装器是 C
中实现的方法。这就是 iter(list).__iter__
(list
是在 C
中实现的对象)和 gen.__iter__
(这里不确定,但生成器可能也是)。
If
__next__
is what both generator and iterator provide (and their sole properties) then what is the difference between generator and iterator?
生成器是迭代器,iter(l)
提供的迭代器也是如此。它是一个迭代器,因为它提供了一个 __next__
方法(通常,当在 for 循环中使用时,它能够提供值直到耗尽)。