Python中“iterable”、“iterator”和“iteration”最基本的定义是什么?
我已经阅读了多个定义,但我无法确定确切的含义,因为它仍然不会下沉。
有人可以帮我用外行术语的3个定义吗?
Python中“iterable”、“iterator”和“iteration”最基本的定义是什么?
我已经阅读了多个定义,但我无法确定确切的含义,因为它仍然不会下沉。
有人可以帮我用外行术语的3个定义吗?
迭代是一个通用术语,用于一个接一个地获取某物的每一项。每当您使用循环(显式或隐式)来遍历一组项目时,这就是迭代。
在 Python 中,iterable和iterator具有特定的含义。
可迭代对象是具有__iter__
返回迭代器的方法的对象,或者定义了__getitem__
可以从零开始获取顺序索引的方法(并IndexError
在索引不再有效时引发 )。因此,可迭代对象是您可以从中获取迭代器的对象。
迭代器是具有next
(Python 2) 或__next__
(Python 3) 方法的对象。
每当你在 Python 中使用for
循环、或map
、列表推导等时,next
都会自动调用该方法从迭代器中获取每个项目,从而完成迭代的过程。
开始学习的好地方是本教程的迭代器部分和标准类型页面的迭代器类型部分。了解基础知识后,请尝试函数式编程 HOWTO 的迭代器部分。
这是我在教授 Python 课程时使用的解释:
一个 ITERABLE 是:
for x in iterable: ...
或iter()
将返回一个 ITERATOR: iter(obj)
或__iter__
返回新 ITERATOR 的对象,或者它可能具有__getitem__
适合索引查找的方法。一个 ITERATOR 是一个对象:
__next__
方法:
StopIteration
__iter__
返回的方法self
)。笔记:
__next__
方法用 Python 2 拼写next
,并且next()
在传递给它的对象上调用该方法。例如:
>>> s = 'cat' # s is an ITERABLE
# s is a str object that is immutable
# s has no state
# s has a __getitem__() method
>>> t = iter(s) # t is an ITERATOR
# t has state (it starts by pointing at the "c"
# t has a next() method and an __iter__() method
>>> next(t) # the next() function returns the next value and advances the state
'c'
>>> next(t) # the next() function returns the next value and advances
'a'
>>> next(t) # the next() function returns the next value and advances
't'
>>> next(t) # next() raises StopIteration to signal that iteration is complete
Traceback (most recent call last):
...
StopIteration
>>> iter(t) is t # the iterator is self-iterable
上面的答案很好,但正如我所看到的那样,对于像我这样的人来说,不要强调足够的区别。
__foo__()
此外,人们往往会通过在前面加上“X 是一个具有方法的对象”之类的定义来获得“太 Pythonic” 。这样的定义是正确的——它们是基于鸭式的哲学,但是在试图理解简单的概念时,对方法的关注往往会介于两者之间。
所以我添加了我的版本。
在自然语言中,
在 Python 中,
iterable是一个可以迭代的对象,简单地说,意味着它可以在迭代中使用,例如for
循环。如何?通过使用迭代器。我会在下面解释。
...而迭代器是一个对象,它定义了如何实际进行迭代——特别是下一个元素是什么。这就是为什么它必须有
next()
方法。
迭代器本身也是可迭代的,区别在于它们的__iter__()
方法返回相同的对象 ( self
),而不管其项目是否已被先前的调用消耗next()
。
那么 Python 解释器在看到for x in obj:
语句时会怎么想呢?
看,一个
for
循环。看起来像是迭代器的工作......让我们来做一个。……有这个obj
人,让我们问问他。“先生
obj
,你有你的迭代器吗?” (...调用iter(obj)
,调用obj.__iter__()
,愉快地分发一个闪亮的新迭代器_i
。)好的,这很简单……让我们开始迭代吧。(
x = _i.next()
……x = _i.next()
)
由于 Mr.obj
在这个测试中成功了(通过让某个方法返回一个有效的迭代器),我们用形容词奖励他:你现在可以称他为“可迭代的 Mr. obj
”。
但是,在简单的情况下,您通常不会从分别拥有 iterator 和 iterable 中受益。所以你只定义一个对象,它也是它自己的迭代器。(Python 并不真正关心_i
分发的obj
不是那么闪亮,而只是它obj
本身。)
这就是为什么在我见过的大多数示例中(以及一遍又一遍让我感到困惑的地方),你可以看到:
class IterableExample(object):
def __iter__(self):
return self
def next(self):
pass
代替
class Iterator(object):
def next(self):
pass
class Iterable(object):
def __iter__(self):
return Iterator()
但是,在某些情况下,您可以从将迭代器与可迭代对象分离中受益,例如当您希望拥有一行项目但有更多“游标”时。例如,当您想使用“当前”和“即将到来的”元素时,您可以为这两个元素设置单独的迭代器。或者从一个巨大的列表中提取多个线程:每个线程都可以有自己的迭代器来遍历所有项目。请参阅上面的@Raymond和@glglgl 的答案。
想象一下你能做什么:
class SmartIterableExample(object):
def create_iterator(self):
# An amazingly powerful yet simple way to create arbitrary
# iterator, utilizing object state (or not, if you are fan
# of functional), magic and nuclear waste--no kittens hurt.
pass # don't forget to add the next() method
def __iter__(self):
return self.create_iterator()
笔记:
我再重复一遍:iterator is not iterable。迭代器不能用作for
循环中的“源”。for
循环主要需要的是(__iter__()
返回带有 的东西next()
)。
当然,for
不是唯一的迭代循环,所以上面也适用于其他一些构造(while
...)。
迭代器next()
可以抛出 StopIteration 来停止迭代。但是,它可以永远迭代或使用其他方式。
在上面的“思考过程”中,_i
并不真正存在。这个名字是我编的。
Python 3.x 有一个小的变化:next()
方法(不是内置的)现在必须调用__next__()
。是的,应该一直都是这样。
也可以这样想:iterable 有数据,iterator 拉下一项
免责声明:我不是任何 Python 解释器的开发人员,所以我真的不知道解释器的“想法”。上面的思考只是展示了我是如何从 Python 新手的其他解释、实验和现实生活中理解这个话题的。
可迭代对象是具有__iter__()
方法的对象。它可能会迭代多次,例如list()
s 和tuple()
s。
迭代器是迭代的对象。它由一个__iter__()
方法返回,通过它自己的__iter__()
方法返回自身并且有一个next()
方法(__next__()
在 3.x 中)。
迭代是调用这个next()
resp的过程。__next__()
直到它升起StopIteration
。
例子:
>>> a = [1, 2, 3] # iterable
>>> b1 = iter(a) # iterator 1
>>> b2 = iter(a) # iterator 2, independent of b1
>>> next(b1)
1
>>> next(b1)
2
>>> next(b2) # start over, as it is the first call to b2
1
>>> next(b1)
3
>>> next(b1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> b1 = iter(a) # new one, start over
>>> next(b1)
1
这是我的备忘单:
sequence
+
|
v
def __getitem__(self, index: int):
+ ...
| raise IndexError
|
|
| def __iter__(self):
| + ...
| | return <iterator>
| |
| |
+--> or <-----+ def __next__(self):
+ | + ...
| | | raise StopIteration
v | |
iterable | |
+ | |
| | v
| +----> and +-------> iterator
| ^
v |
iter(<iterable>) +----------------------+
|
def generator(): |
+ yield 1 |
| generator_expression +-+
| |
+-> generator() +-> generator_iterator +-+
测验:你知道如何...
__iter__()
方法可以实现为生成器吗?__next__
方法的迭代不一定是迭代器?答案:
__iter__
方法。拥有__iter__
就足以成为一个可迭代的。因此,每个迭代器都是可迭代的。当__iter__
被调用时,它应该返回一个迭代器(return <iterator>
在上图中)。调用生成器会返回一个生成器迭代器,它是一种迭代器。
class Iterable1:
def __iter__(self):
# a method (which is a function defined inside a class body)
# calling iter() converts iterable (tuple) to iterator
return iter((1,2,3))
class Iterable2:
def __iter__(self):
# a generator
for i in (1, 2, 3):
yield i
class Iterable3:
def __iter__(self):
# with PEP 380 syntax
yield from (1, 2, 3)
# passes
assert list(Iterable1()) == list(Iterable2()) == list(Iterable3()) == [1, 2, 3]
这是一个例子:
class MyIterable:
def __init__(self):
self.n = 0
def __getitem__(self, index: int):
return (1, 2, 3)[index]
def __next__(self):
n = self.n = self.n + 1
if n > 3:
raise StopIteration
return n
# if you can iter it without raising a TypeError, then it's an iterable.
iter(MyIterable())
# but obviously `MyIterable()` is not an iterator since it does not have
# an `__iter__` method.
from collections.abc import Iterator
assert isinstance(MyIterable(), Iterator) # AssertionError
我不知道它是否对任何人有帮助,但我总是喜欢在脑海中形象化概念以更好地理解它们。因此,当我有一个小儿子时,我用砖块和白皮书将可迭代/迭代器概念可视化。
假设我们在黑暗的房间里,地板上有我儿子的砖块。不同大小、颜色的砖块,现在都无所谓了。假设我们有 5 块这样的砖块。这 5 块积木可以描述为一个对象——比如说积木套件。我们可以用这个积木套件做很多事情——可以拿一个然后拿第二个然后第三个,可以改变积木的位置,把第一块积木放在第二块上面。我们可以用这些做很多事情。因此,这个积木工具包是一个可迭代的对象或序列,因为我们可以遍历每块积木并对其进行处理。我们只能像我的小儿子那样做——我们一次只能玩一块砖。所以我再一次想象自己这个积木套件是一个可迭代的。
现在请记住,我们在黑暗的房间里。或者几乎是黑暗的。问题是我们没有清楚地看到这些砖块,它们是什么颜色,什么形状等。所以即使我们想对它们做点什么——也就是遍历它们——我们也不知道是什么以及如何做,因为它是太黑了。
我们可以做的是靠近第一个砖块——作为砖块套件的元素——我们可以放一张白色荧光纸,以便我们看到第一个砖块元素在哪里。每次我们从套件中取出一块砖,我们将白纸替换为下一块砖,以便能够在黑暗的房间中看到它。这张白纸只不过是一个迭代器。它也是一个对象。但是一个我们可以使用我们可迭代对象的元素工作和玩耍的对象 - 积木套件。
顺便说一句,这解释了我在 IDLE 中尝试以下操作并得到 TypeError 时的早期错误:
>>> X = [1,2,3,4,5]
>>> next(X)
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
next(X)
TypeError: 'list' object is not an iterator
这里的清单 X 是我们的积木套件,但不是一张白纸。我需要先找到一个迭代器:
>>> X = [1,2,3,4,5]
>>> bricks_kit = [1,2,3,4,5]
>>> white_piece_of_paper = iter(bricks_kit)
>>> next(white_piece_of_paper)
1
>>> next(white_piece_of_paper)
2
>>>
不知道它是否有帮助,但它帮助了我。如果有人可以确认/纠正这个概念的可视化,我将不胜感激。这将帮助我了解更多。
可迭代:- 可迭代的东西是可迭代的;像列表,字符串等序列。它也有__getitem__
方法或__iter__
方法。现在,如果我们iter()
在该对象上使用函数,我们将得到一个迭代器。
Iterator :- 当我们从iter()
函数中获取迭代器对象时;我们调用__next__()
方法(在python3中)或简单地next()
(在python2中)来一个一个地获取元素。此类或此类的实例称为迭代器。
来自文档:-
迭代器的使用遍及并统一了 Python。在幕后,for 语句调用 iter()
容器对象。该函数返回一个迭代器对象,该对象定义了 __next__()
一次访问容器中元素的方法。当没有更多元素时, __next__()
引发一个 StopIteration 异常,告诉 for 循环终止。您可以 使用 内置函数调用该__next__()
方法 ;next()
这个例子展示了它是如何工作的:
>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
next(it)
StopIteration
前一类:-
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
... print(char)
...
m
a
p
s
迭代器是实现iter和next方法的对象。如果定义了这些方法,我们可以使用 for 循环或理解。
class Squares:
def __init__(self, length):
self.length = length
self.i = 0
def __iter__(self):
print('calling __iter__') # this will be called first and only once
return self
def __next__(self):
print('calling __next__') # this will be called for each iteration
if self.i >= self.length:
raise StopIteration
else:
result = self.i ** 2
self.i += 1
return result
迭代器会耗尽。这意味着在您迭代项目之后,您不能重复,您必须创建一个新对象。假设您有一个类,其中包含城市属性并且您想要迭代。
class Cities:
def __init__(self):
self._cities = ['Brooklyn', 'Manhattan', 'Prag', 'Madrid', 'London']
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._cities):
raise StopIteration
else:
item = self._cities[self._index]
self._index += 1
return item
Cities 类的实例是一个迭代器。但是,如果您想重复城市,则必须创建一个新对象,这是一项昂贵的操作。您可以将该类分为 2 个类:一个返回城市,第二个返回一个迭代器,该迭代器将城市作为初始参数。
class Cities:
def __init__(self):
self._cities = ['New York', 'Newark', 'Istanbul', 'London']
def __len__(self):
return len(self._cities)
class CityIterator:
def __init__(self, city_obj):
# cities is an instance of Cities
self._city_obj = city_obj
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._city_obj):
raise StopIteration
else:
item = self._city_obj._cities[self._index]
self._index += 1
return item
现在如果我们需要创建一个新的迭代器,我们不必再次创建数据,即城市。我们创建城市对象并将其传递给迭代器。但我们仍在做额外的工作。我们可以通过只创建一个类来实现这一点。
Iterable是一个实现可迭代协议的 Python 对象。它只需要__iter__()
返回迭代器对象的新实例。
class Cities:
def __init__(self):
self._cities = ['New York', 'Newark', 'Istanbul', 'Paris']
def __len__(self):
return len(self._cities)
def __iter__(self):
return self.CityIterator(self)
class CityIterator:
def __init__(self, city_obj):
self._city_obj = city_obj
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._city_obj):
raise StopIteration
else:
item = self._city_obj._cities[self._index]
self._index += 1
return item
Iterators 有__iter__
和__next__
,iterables 有__iter__
,所以我们可以说 Iterators 也是 iterables 但它们是会耗尽的 iterables。另一方面,可迭代对象永远不会耗尽,因为它们总是返回一个新的迭代器,然后用于迭代
您注意到可迭代代码的主要部分在迭代器中,而可迭代本身只不过是一个额外的层,它允许我们创建和访问迭代器。
Python 有一个内置函数iter()调用__iter__()
. 当我们迭代一个可迭代对象时,Python 调用iter()返回一个迭代器,然后它开始使用__next__()
迭代器来迭代数据。
请注意,在上面的示例中,Cities 创建了一个可迭代但它不是序列类型,这意味着我们无法通过索引获取城市。为了解决这个问题,我们应该添加__get_item__
到 Cities 类中。
class Cities:
def __init__(self):
self._cities = ['New York', 'Newark', 'Budapest', 'Newcastle']
def __len__(self):
return len(self._cities)
def __getitem__(self, s): # now a sequence type
return self._cities[s]
def __iter__(self):
return self.CityIterator(self)
class CityIterator:
def __init__(self, city_obj):
self._city_obj = city_obj
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._city_obj):
raise StopIteration
else:
item = self._city_obj._cities[self._index]
self._index += 1
return item
Iterables有一个
__iter__
方法,每次都实例化一个新的迭代器。迭代器实现了一个
__next__
返回单个项目的__iter__
方法和一个返回self
.因此,迭代器也是可迭代的,但可迭代的不是迭代器。
Luciano Ramalho,流利的 Python。
iterable = [1, 2]
iterator = iter(iterable)
print(iterator.__next__())
print(iterator.__next__())
所以,
iterable
是一个可以循环的对象。例如列表、字符串、元组等。
在我们的对象上使用该iter
函数iterable
将返回一个迭代器对象。
现在这个迭代器对象有一个名为的方法__next__
(在 Python 3 中,或者只是next
在 Python 2 中),您可以通过它访问 iterable 的每个元素。
因此,上述代码的输出将是:
1
2
在处理迭代器和迭代器之前,决定迭代器和迭代器的主要因素是序列
序列:序列是数据的集合
Iterable:Iterable 是支持__iter__
方法的序列类型对象。
Iter 方法:Iter 方法将序列作为输入并创建一个称为迭代器的对象
迭代器:迭代器是调用下一个方法并横穿序列的对象。在调用下一个方法时,它返回当前遍历的对象。
例子:
x=[1,2,3,4]
x 是一个由数据集合组成的序列
y=iter(x)
仅当 x 对象具有 iter 方法时调用iter(x)
它才会返回迭代器,否则会引发异常。如果它返回迭代器,则 y 分配如下:
y=[1,2,3,4]
由于 y 是一个迭代器,因此它支持next()
方法
在调用 next 方法时,它会一个一个地返回列表中的各个元素。
在返回序列的最后一个元素后,如果我们再次调用 next 方法,它会引发 StopIteration 错误
例子:
>>> y.next()
1
>>> y.next()
2
>>> y.next()
3
>>> y.next()
4
>>> y.next()
StopIteration
要查看对象是否具有此方法iter (),我们可以使用以下函数。
ls = ['hello','bye']
print(dir(ls))
输出
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
如您所见,iter () 意味着它是一个可迭代对象,但不包含作为迭代器对象特征的next () 方法
每当您在 Python 中使用 for 循环或映射或列表推导时,都会自动调用next方法以从迭代中获取每个项目
其他人已经全面解释了,什么是iterable和iterator,所以我将尝试对generators做同样的事情。
恕我直言,理解生成器的主要问题是“生成器”这个词的使用令人困惑,因为这个词有两种不同的含义:
yield
在其主体中带有语句),生成器作为第一种类型的工具:
In[2]: def my_generator():
...: yield 100
...: yield 200
In[3]: my_generator
Out[3]: <function __main__.my_generator()>
In[4]: type(my_generator)
Out[4]: function
生成器作为一个结果(即一个迭代器)使用这个工具:
In[5]: my_iterator = my_generator()
In[6]: my_iterator
Out[6]: <generator object my_generator at 0x00000000053EAE48>
In[7]: type(my_iterator)
Out[7]: generator
生成器作为第二类工具——与该工具生成的迭代器没有区别:
In[8]: my_gen_expression = (2 * i for i in (10, 20))
In[9]: my_gen_expression
Out[9]: <generator object <genexpr> at 0x000000000542C048>
In[10]: type(my_gen_expression)
Out[10]: generator
这是另一个使用collections.abc
. 此视图可能在第二次或更晚时有用。
从collections.abc
我们可以看到以下层次结构:
builtins.object
Iterable
Iterator
Generator
即Generator 派生自Iterator 派生自Iterable 派生自基础对象。
因此,
[1, 2, 3]
andrange(10)
是可迭代的,但不是迭代器。 x = iter([1, 2, 3])
是一个迭代器和一个可迭代对象。iter()
迭代器或生成器会返回自身。因此,如果it
是一个迭代器,那么iter(it) is it
是真。[2 * x for x in nums]
或类似 for 循环的for x in nums:
行为就像iter()
在可迭代 () 上调用nums
,然后nums
使用该迭代器进行迭代。因此,以下所有内容在功能上都是等效的(例如,nums=[1, 2, 3]
):
for x in nums:
for x in iter(nums):
for x in iter(iter(nums))
:for x in iter(iter(iter(iter(iter(nums))))):
对我来说,Python 的词汇表对这些问题最有帮助,例如对于 iterable,它说:
一个能够一次返回其成员的对象。可迭代对象的示例包括所有序列类型(例如 list、str 和 tuple)和一些非序列类型,例如 dict、文件对象以及您使用iter () 方法或getitem () 方法定义的任何类的对象实现序列语义。
Iterables 可用于 for 循环和许多其他需要序列的地方(zip()、map()、...)。当可迭代对象作为参数传递给内置函数 iter() 时,它会返回该对象的迭代器。此迭代器适用于遍历一组值。使用迭代器时,通常不需要调用 iter() 或自己处理迭代器对象。for 语句会自动为您执行此操作,创建一个临时的未命名变量以在循环期间保存迭代器。另请参见迭代器、序列和生成器。
这是示例: class Square(): def init (self, max): self.max =max self.power =0 def iter (self): return self def next (self):
if(self.power<=self.max):
result = 2**self.power
self.power +=1
return result
else:
raise StopIteration
square = Square(5) for i in square: print(i)
在 Python 中,一切都是对象。当一个对象被称为可迭代时,这意味着您可以将对象作为一个集合单步执行(即迭代)。
例如数组是可迭代的。您可以使用 for 循环逐步遍历它们,然后从索引 0 转到索引 n,n 是数组对象的长度减 1。
字典(键/值对,也称为关联数组)也是可迭代的。您可以逐步查看他们的密钥。
显然,不是集合的对象是不可迭代的。例如,一个 bool 对象只有一个值,True 或 False。它是不可迭代的(它是一个可迭代的对象是没有意义的)。