何谓可迭代对象,何谓迭代器,它们之间关系如何?先谈谈这几个概念
我们已经知道,可以直接作用于 for循环 的数据类型有以下几种:
一类是集合数据类型,如 list、tuple、dict、set、str 等
一类是 generator,包括 生成器 和 带 yield 的 generator function
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
可以使用isinstance()判断一个对象是否是Iterable对象
如下,常见的数据类型列表、字典、元组、集合、字符串都是可迭代的对象,但整数 666 不可迭代
In [90]: from collections.abc import Iterable
In [91]: isinstance([],Iterable)
Out[91]: True
In [92]: isinstance({},Iterable)
Out[92]: True
In [93]: isinstance('eva',Iterable)
Out[93]: True
In [94]: isinstance((1,),Iterable)
Out[94]: True
In [95]: isinstance(666,Iterable)
Out[95]: False
In [96]: isinstance({3},Iterable)
Out[96]: True
In [97]: isinstance({'a':1},Iterable)
Out[97]: True
迭代器遵守迭代器协议,迭代器协议定义如下:
A Python iterator object must implement two special methods, __iter__() and __next__(), collectively called the iterator protocol.
Python迭代器对象必须实现__iter __()和__next __()这两个特殊方法,统称为迭代器协议。
通过 dir(obj) 或 obj.__dir__() 方法来查看 该对象的属性方法
In [106]: x = [1,2,3]
In [107]: y = iter(x)
In [108]: dir(x) # dir(obj) is same as obj.__dir__()
Out[108]:
['__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']
In [109]:
In [109]: y.__dir__() # dir(obj) is same as obj.__dir__()
Out[109]:
['__getattribute__',
'__iter__',
'__next__',
'__length_hint__',
'__reduce__',
'__setstate__',
'__doc__',
'__repr__',
'__hash__',
'__str__',
'__setattr__',
'__delattr__',
'__lt__',
'__le__',
'__eq__',
'__ne__',
'__gt__',
'__ge__',
'__init__',
'__new__',
'__reduce_ex__',
'__subclasshook__',
'__init_subclass__',
'__format__',
'__sizeof__',
'__dir__',
'__class__']
In [110]:
通过迭代器进行迭代
使用 next() 函数手动遍历迭代器的所有项目。当我们到达末尾并且没有更多数据要返回时,它将引发StopIteration Exception。示例如下:
注意:next(obj) is same as obj.__next__()
# define a list
my_list = [4, 7, 0, 3]
# get an iterator using iter()
my_iter = iter(my_list)
# iterate through it using next()
# Output: 4
print(next(my_iter))
# Output: 7
print(next(my_iter))
# next(obj) is same as obj.__next__()
# Output: 0
print(my_iter.__next__())
# Output: 3
print(my_iter.__next__())
# This will raise error, no items left
next(my_iter)
输出:
4
7
0
3
Traceback (most recent call last):
File "" , line 24, in <module>
next(my_iter)
StopIteration
再如下所示,x 是可迭代的,而 y 是迭代器单独的一个实例,y 可以迭代 x 产生值,在迭代结束引发 StopIteration表示迭代结束
In [98]: x = [1, 2, 3]
In [99]: y = iter(x)
In [100]: next(y)
Out[100]: 1
In [101]: next(y)
Out[101]: 2
In [102]: next(y)
Out[102]: 3
In [103]: next(y)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-103-81b9d2f0f16a> in <module>
----> 1 next(y)
StopIteration:
In [104]: type(x)
Out[104]: list
In [105]: type(y)
Out[105]: list_iterator
In [106]:
coding 实际中,列表写 for 列表 遍历,一般如下:
x = [1, 2, 3]
for elem in x:
...
其实际过程是这样的
# create an iterator object from that iterable
iter_obj = iter(iterable)
# infinite loop
while True:
try:
# get the next item
element = next(iter_obj)
# do something with element
except StopIteration:
# if StopIteration is raised, break from loop
break
iterable_value = 'iterables' # 可迭代的字符串
iterable_obj = iter(iterable_value) # 初始化迭代器
while True:
try:
# Iterate by calling next
item = next(iterable_obj)
print(item)
except StopIteration:
# exception will happen when iteration will over
break
输出:
i
t
e
r
a
b
l
e
s
python中从头开始构建迭代器还是很容易的,只需要实现__iter __() 和__next __() 方法即可。 __iter __() 方法返回迭代器对象本身,如果需要的话,也可以执行一些初始化操作。 __next __() 方法必须返回序列中的下一项,在到达末尾以及随后的调用中,它必须引发StopIteration
示例如下:
将在每次迭代中提供下一个2的幂,幂指数从零开始一直到用户设置的数字
注意:next(obj) is same as obj.__next__()
class PowTwo:
"""Class to implement an iterator
of powers of two"""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
# create an object
numbers = PowTwo(3)
# create an iterable from the object
i = iter(numbers)
# Using next to get to the next iterator element
print(next(i))
print(next(i))
print(i.__next__())
print(next(i))
print(i.__next__())
输出:
1
2
4
8
Traceback (most recent call last):
File "/home/xxx/Desktop/Untitled-1.py", line 32, in <module>
print(next(i))
File "" , line 18, in __next__
raise StopIteration
StopIteration
同样可以使用for循环来迭代 这个迭代器类
>>> for i in PowTwo(5):
... print(i)
...
1
2
4
8
16
32
不必耗尽迭代器对象中的项目,可以有无限迭代器(永无止境)。处理此类迭代器时,我们必须小心。下边演示一个无限迭代器的简单示例:
可以使用两个参数调用内置函数iter()函数,其中第一个参数必须是可调用对象(函数),第二个参数是前哨,迭代器将调用此函数,直到返回的值等于哨兵。
It is not necessary that the item in an iterator object has to be exhausted. There can be infinite iterators (which never ends). We must be careful when handling such iterators.
Here is a simple example to demonstrate infinite iterators.
The built-in function iter() function can be called with two arguments where the first argument must be a callable object (function) and second is the sentinel. The iterator calls this function until the returned value is equal to the sentinel.
>>> int()
0
>>> int()
0
>>> int()
0
>>> inf = iter(int,1)
>>> next(inf)
0
>>> next(inf)
0
我们可以看到 int() 函数始终返回0。因此,将其作为 iter(int,1)传递将返回一个迭代器,该迭代器调用 int() 直到返回值等于1。然鹅这永远不会发生,并且我们得到一个无限迭代器。
We can see that the int() function always returns 0. So passing it as iter(int,1) will return an iterator that calls int() until the returned value equals 1. This never happens and we get an infinite iterator.
我们还可以构建自己的无限迭代器。 理论上,以下迭代器将返回所有奇数。
We can also build our own infinite iterators. The following iterator will, theoretically, return all the odd numbers.
class InfIter:
"""Infinite iterator to return all
odd numbers"""
def __iter__(self):
self.num = 1
return self
def __next__(self):
num = self.num
self.num += 2
return num
运行结果举例如下:
>>> a = iter(InfIter())
>>> next(a)
1
>>> next(a)
3
>>> next(a)
5
>>> next(a)
7
依此类推…
And so on…
在迭代这些类型的无限迭代器时,请小心包含终止条件。
Be careful to include a terminating condition, when iterating over these types of infinite iterators.
使用迭代器的优点是节省了资源。 如上所示,我们无需将整个数字系统存储在内存中就可以获得所有奇数。 从理论上讲,我们可以在有限内存中包含无限项。
The advantage of using iterators is that they save resources. Like shown above, we could get all the odd numbers without storing the entire number system in memory. We can have infinite items (theoretically) in finite memory.