为什么生成器是详尽的而 Lists/Tuples 不是?
Why Generators are exhaustive and Lists/Tuples are not?
首先,我不得不说,在开始这篇文章之前,我阅读了很多 SO 帖子,因为我找不到我要找的东西,或者我可能不明白。
就这样吧
我有点了解 Iterables 和 Iterators 是什么。因此,任何包含项目的容器对象,如 Lists/Tuples/Sets,您可以对其进行迭代,称为 Iterables。现在迭代 Iterables 你需要迭代器,它发生的方式是因为 __iter__
方法为你提供类型的迭代器对象,然后调用迭代器对象上的 __next__
来提取值。
因此,要使任何对象可迭代,您需要定义 iter 和 next 方法,我想列表也是如此.但是我最近发现了奇怪的部分。
l1 = [1,2,3]
hasattr(l1, "__next__")
Out[42]: False
g = (x for x in range(3))
hasattr(g, "__next__")
Out[44]: True
现在因为列表确实支持 Iterator 协议,为什么 __next__
方法在它们的实现中缺失,如果它确实缺失,那么列表的迭代如何工作?
list_iterator = iter(l1)
next(list_iterator)
Out[46]: 1
next(list_iterator)
Out[47]: 2
next(list_iterator)
Out[48]: 3
next(list_iterator)
Traceback (most recent call last):
File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-49-56e733bbb896>", line 1, in <module>
next(list_iterator)
StopIteration
gen0_iterator = iter(g)
gen_iterator = iter(g)
next(gen_iterator)
Out[57]: 0
next(gen_iterator)
Out[58]: 1
next(gen_iterator)
Out[59]: 2
next(gen_iterator)
Traceback (most recent call last):
File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-60-83622dd5d1b9>", line 1, in <module>
next(gen_iterator)
StopIteration
gen_iterator1 = iter(g)
next(gen_iterator1)
Traceback (most recent call last):
File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-62-86f9b3cc341f>", line 1, in <module>
next(gen_iterator1)
StopIteration
我为列表创建了一个迭代器,然后调用它的 next 方法来获取元素并且它起作用了。
现在,如果前面的 hasattr(a, "__next__")
returns 一个 False
那么我们如何能够调用迭代器对象上的 next 方法来获取列表。
现在让我思考所有这些的原始问题,无论我迭代列表多少次,它都不会耗尽并且调用 iter()
返回一个新的迭代器对象每次,但在生成器的情况下,这不会发生,一旦生成器耗尽,无论你调用多少次 iter()
它总是会给你已经引发 [=21= 的同一个对象] 例外,这也是真的,因为迭代器一旦引发 StopIteration
,它总是会,但为什么它不会发生在列表中。
此外,这与 python 文档对 conatiner.__ iter__ that container.__iter__
gives you the iterator object for the type and iterator.__ iter__ 所说的同步,iterator.__iter__
为您提供了迭代器对象本身,这正是调用 iter()
在生成器上 returns 一遍又一遍地使用相同的对象。但为什么,更重要的是如何?
这里还要注意的一件事是
isinstance(l1 , collections.Iterator)
Out[65]: False
isinstance(g , collections.Iterator)
Out[66]: True
所以这表明存在一些实现差异 b/w 可迭代对象和迭代器,但我找不到任何此类细节,因为它们都实现了 __iter__
和 __next__
方法,所以这种行为变化从何而来。因此,__iter__
for iterables returns 与 iterables(generators)的 __iter__
返回的内容不同。如果有人可以用一些 __iter__
的示例来解释 Iterables 和 Iterataors,那将非常有帮助。最后是关于 yield
的一些困惑,因为这是使普通函数成为生成器(因此是一种迭代器)的神奇词,所以 `yield 的 __iter__
和 __next__
看起来像什么.
我已经尽力解释了这个问题,但如果还有什么遗漏,请告诉我,我会尽力澄清我的问题。
这有点不同。 iterables 有一个 __iter__
方法,returns 一个迭代器。 迭代器 有一个 __next__
方法(通常也有 __iter__
以便 iter()
对它们起作用——但这不是必需的)。
列表是可迭代的:
>>> l = [1,2,3]
>>> hasattr(l, "__iter__")
True
>>> hasattr(l, "__next__")
False
>>> l_iter = iter(l)
>>> hasattr(l_iter, "__next__")
True
>>> hasattr(l_iter, "__iter__")
True
>>> l_iter == iter(l_iter)
True
并为您提供新的迭代器,每次您使用它们时 运行 通过 e
>>> list(l)
[1, 2, 3]
>>> list(l)
[1, 2, 3]
>>> l_iter = iter(l)
>>> list(l_iter)
[1, 2, 3]
>>> list(l_iter)
[]
each time you use them
>>> list(l)
[1, 2, 3]
>>> list(l)
[1, 2, 3]
>>> iter(l) == iter(l)
False
但是列表迭代器本身就是一枪
>>> l_iter = iter(l)
>>> list(l_iter)
[1, 2, 3]
>>> list(l_iter)
[]
generator是iterator,不是iterable,也是one shot
>>> g = (x for x in range(3))
>>> hasattr(g, "__iter__")
True
>>> hasattr(g, "__next__")
True
>>> g == iter(g)
True
>>>
>>> list(g)
[0, 1, 2]
>>> list(g)
[]
首先,我不得不说,在开始这篇文章之前,我阅读了很多 SO 帖子,因为我找不到我要找的东西,或者我可能不明白。 就这样吧
我有点了解 Iterables 和 Iterators 是什么。因此,任何包含项目的容器对象,如 Lists/Tuples/Sets,您可以对其进行迭代,称为 Iterables。现在迭代 Iterables 你需要迭代器,它发生的方式是因为 __iter__
方法为你提供类型的迭代器对象,然后调用迭代器对象上的 __next__
来提取值。
因此,要使任何对象可迭代,您需要定义 iter 和 next 方法,我想列表也是如此.但是我最近发现了奇怪的部分。
l1 = [1,2,3]
hasattr(l1, "__next__")
Out[42]: False
g = (x for x in range(3))
hasattr(g, "__next__")
Out[44]: True
现在因为列表确实支持 Iterator 协议,为什么 __next__
方法在它们的实现中缺失,如果它确实缺失,那么列表的迭代如何工作?
list_iterator = iter(l1)
next(list_iterator)
Out[46]: 1
next(list_iterator)
Out[47]: 2
next(list_iterator)
Out[48]: 3
next(list_iterator)
Traceback (most recent call last):
File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-49-56e733bbb896>", line 1, in <module>
next(list_iterator)
StopIteration
gen0_iterator = iter(g)
gen_iterator = iter(g)
next(gen_iterator)
Out[57]: 0
next(gen_iterator)
Out[58]: 1
next(gen_iterator)
Out[59]: 2
next(gen_iterator)
Traceback (most recent call last):
File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-60-83622dd5d1b9>", line 1, in <module>
next(gen_iterator)
StopIteration
gen_iterator1 = iter(g)
next(gen_iterator1)
Traceback (most recent call last):
File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-62-86f9b3cc341f>", line 1, in <module>
next(gen_iterator1)
StopIteration
我为列表创建了一个迭代器,然后调用它的 next 方法来获取元素并且它起作用了。
现在,如果前面的
hasattr(a, "__next__")
returns 一个False
那么我们如何能够调用迭代器对象上的 next 方法来获取列表。现在让我思考所有这些的原始问题,无论我迭代列表多少次,它都不会耗尽并且调用
iter()
返回一个新的迭代器对象每次,但在生成器的情况下,这不会发生,一旦生成器耗尽,无论你调用多少次iter()
它总是会给你已经引发 [=21= 的同一个对象] 例外,这也是真的,因为迭代器一旦引发StopIteration
,它总是会,但为什么它不会发生在列表中。
此外,这与 python 文档对 conatiner.__ iter__ that container.__iter__
gives you the iterator object for the type and iterator.__ iter__ 所说的同步,iterator.__iter__
为您提供了迭代器对象本身,这正是调用 iter()
在生成器上 returns 一遍又一遍地使用相同的对象。但为什么,更重要的是如何?
这里还要注意的一件事是
isinstance(l1 , collections.Iterator)
Out[65]: False
isinstance(g , collections.Iterator)
Out[66]: True
所以这表明存在一些实现差异 b/w 可迭代对象和迭代器,但我找不到任何此类细节,因为它们都实现了 __iter__
和 __next__
方法,所以这种行为变化从何而来。因此,__iter__
for iterables returns 与 iterables(generators)的 __iter__
返回的内容不同。如果有人可以用一些 __iter__
的示例来解释 Iterables 和 Iterataors,那将非常有帮助。最后是关于 yield
的一些困惑,因为这是使普通函数成为生成器(因此是一种迭代器)的神奇词,所以 `yield 的 __iter__
和 __next__
看起来像什么.
我已经尽力解释了这个问题,但如果还有什么遗漏,请告诉我,我会尽力澄清我的问题。
这有点不同。 iterables 有一个 __iter__
方法,returns 一个迭代器。 迭代器 有一个 __next__
方法(通常也有 __iter__
以便 iter()
对它们起作用——但这不是必需的)。
列表是可迭代的:
>>> l = [1,2,3]
>>> hasattr(l, "__iter__")
True
>>> hasattr(l, "__next__")
False
>>> l_iter = iter(l)
>>> hasattr(l_iter, "__next__")
True
>>> hasattr(l_iter, "__iter__")
True
>>> l_iter == iter(l_iter)
True
并为您提供新的迭代器,每次您使用它们时 运行 通过 e
>>> list(l)
[1, 2, 3]
>>> list(l)
[1, 2, 3]
>>> l_iter = iter(l)
>>> list(l_iter)
[1, 2, 3]
>>> list(l_iter)
[]
each time you use them
>>> list(l)
[1, 2, 3]
>>> list(l)
[1, 2, 3]
>>> iter(l) == iter(l)
False
但是列表迭代器本身就是一枪
>>> l_iter = iter(l)
>>> list(l_iter)
[1, 2, 3]
>>> list(l_iter)
[]
generator是iterator,不是iterable,也是one shot
>>> g = (x for x in range(3))
>>> hasattr(g, "__iter__")
True
>>> hasattr(g, "__next__")
True
>>> g == iter(g)
True
>>>
>>> list(g)
[0, 1, 2]
>>> list(g)
[]