写在前面:
文章参考自如下博客, 在这里表示感谢。
https://www.cnblogs.com/wangcoo/p/10018363.html
python的可迭代对象, 迭代操作与迭代器
什么是迭代 和 迭代器
迭代是一种访问集合元素的方式,
迭代器是从第一个元素开始访问, 直到所有的元素全部访问完毕, 并且可以记住 遍历位置 的object。
(具体的后面还有写)
什么是可迭代对象
我们都知道, list, tuple, string 这些类型的数据 可以使用 for...in... 的循环语法 从其中依次拿到数据进⾏使⽤,
上面说了 , 我们把这样的过程称为遍历,也叫迭代。
那么 , 满足这种操作的对象,我们就称其为可迭代对象。
但是并不是所有的 对象 都可以执行这样的 for ... in ... 的操作,
譬如 int 类型的数据 就不可以执行 for i in 15 : 的这种操作, 所以我们说, Int 类型的对象不是一个可迭代对象。
你可能会觉得 int类型的对象 只是一个数字, 当然是不可迭代的了,其实
即使一个数据类型 包含多条数据,他也不一定是可迭代对象。
实例如下:
# 我们⾃定义⼀个容器MyList⽤来存放数据,可以通过add⽅法向其中添加数据
>>> class MyList(object):
... def __init__(self):
... self.container = []
def add(self, item):
... self.container.append(item)
>>> mylist = MyList()
>>> mylist.add(1)
>>> mylist.add(2)
>>> mylist.add(3)
执行结果
>>> for num in mylist:
... print(num) ...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'MyList' object is not iterable
我们⾃定义了⼀个容器类型MyList,在将⼀个存放了多个数据的MyList对象放到for...in...的语句中,
发现for...in...并不能从中依次取出⼀条数据返回给我们,
也就说 我们随便封装了⼀个可以存放多条数据的类型 却并不能被迭代使⽤。
(我们可以将其改写为可迭代的对象, 方法会在下面慢慢说)
再次总结
我们把那些可以通过 for...in... 这类语句迭代读取⼀条数 据供我们使⽤的对象称之为可迭代对象(Iterable)
如何判断这个对象是否属于可迭代对象
使用 isinstance() -------> isinstance( object, Iterable )
走进 迭代器 ------> 什么是 可迭代对象的本质
我们分析一下 对可迭代对象 进行迭代 的这个过程:
每迭代一次(即在for...in...中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。
-
在对 可迭代对象 执行迭代操作 的时候(即使用for 循环遍历这个可迭代对象的时候),我们可以选择 对这之中的任意一次迭代 添加我们想要的操作,
那么,这就需要 在这个过程中有一个“人”去记录每次访问到了第几条数据,以便于我们的使用 , 并且确保每次迭代都可以返回下一条数据。
我们将 这个能帮助我们 记录数据迭代过程的 “人” 称为迭代器(Iterator)。
可迭代对象的本质就是 能够向我们提供一个这样的中间 “人” , 即迭代器, 来帮助我们对其进行迭代遍历使用。
(也就是说 你这个对象是不是一个可迭代的对象, 终极的依据就是 它是否有这个迭代器,
python 中自带的可迭代对象都是有这个迭代器的, 我们上面创建的 MyList() 类之所以不是可迭代对象,
就是因为他没用迭代器, )
关于迭代器
可迭代对象通过__iter__
方法向我们提供一个迭代器,我们在对一个可迭代对象 进行迭代操作的时候,
实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.
那么也就是说,一个具备了__iter__
方法的对象,就是一个可迭代对象。
>>> class MyList(object):
... def __init__(self):
... self.container = []
... def add(self, item):
... self.container.append(item)
... def __iter__(self):
... """返回一个迭代器"""
... # 我们暂时忽略如何构造一个迭代器对象
... pass
...
>>> mylist = MyList()
>>> from collections import Iterable
>>> isinstance(mylist, Iterable)
True
>>>
# 这回测试发现添加了__iter__方法的mylist对象已经是一个可迭代对象了
__iter__ 就是迭代器!!!!!!!!!!
如何填满上面 def __iter__(self): 的空架子?
--> 使得我们可以将 我们创建的 MyList() 类 变成真正的可迭代对象。
迭代器的功能是获取下一个, 获取下一条。 那么我们就需要实现这个功能@@@@@
先来看一下 python 自带的迭代器是怎么实现这个功能的。
list、tuple等都是可迭代对象,我们可以通过 iter() 函数获取这些可迭代对象的迭代器。
iter()函数实际上就是调用了可迭代对象的__iter__
方法。
然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。
也就是说 python 的迭代器是通过 next 函数来实现 获取下一条的这个功能的。
>>> li = [11, 22, 33, 44, 55]
# iter()函数实际上就是调用了可迭代对象的__iter__方法。
>>> li_iter = iter(li)
>>> next(li_iter)
11
>>> next(li_iter)
22
>>> next(li_iter)
33
>>> next(li_iter)
44
>>> next(li_iter)
55
>>> next(li_iter)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
'''
注意,当我们已经迭代完最后一个数据之后,再次调用next()函数会抛出StopIteration的异常,
来告诉我们所有数据都已迭代完成,不用再执行next()函数了。**
'''
实际上,使用next()函数的本质是 调用迭代器对象的__next__
方法 。
(Python3中是对象的__next__
方法,Python2中是对象的next()方法)
总结,
python 通过next() 方法 实现了迭代器iter 的获取下一个, 获取下一条的功能,
所以,我们要想构造一个可用的迭代器, 而不是前面的空架子,就要实现它的__next__
方法。
但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__
方法,
而__iter__
方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__
方法返回自身即可。
(上面这两行 我也不懂是什么意思)
这段代码是 填充上面的空架子, 也就是实现 next 的方法:
一个实现了iter方法和next方法的对象,就是迭代器。
class MyList(object):
"""自定义的一个可迭代对象"""
def __init__(self):
self.items = []
def add(self, val):
self.items.append(val)
def __iter__(self):
myiterator = MyIterator(self)
return myiterator
class MyIterator(object):
"""自定义的供上面可迭代对象使用的一个迭代器"""
def __init__(self, mylist):
self.mylist = mylist
# current用来记录当前访问到的位置
self.current = 0
def __next__(self):
if self.current < len(self.mylist.items):
item = self.mylist.items[self.current]
self.current += 1
return item
else:
raise StopIteration
def __iter__(self):
return self
if __name__ == '__main__':
mylist = MyList()
mylist.add(1)
mylist.add(2)
mylist.add(3)
mylist.add(4)
mylist.add(5)
for num in mylist:
print(num)
有了next 方法, 这就是一个货真价值的 迭代器了 !!!!
for...in...循环的本质
for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,
然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
迭代器的应用
我们发现迭代器最核心的功能就是可以通过next()函数的调用来返回下一个数据值。
如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定的规律计算生成的,
那么也就意味着可以不用再依赖一个已有的数据集合,也就是说不用再将所有要迭代的数据都一次性缓存下来供后续依次读取,这样可以节省大量的存储(内存)空间。