这两天刚好学习了这一块的知识,就借机会整理下这三个概念的关系啦。
废话不多说,开始吧!
关于可迭代对象的理解
说迭代器之前,必须要说可迭代对象。
什么是可迭代对象?
顾名思义,就是一个对象能够被迭代的使用。
我们可以利用Python提供的模块collections来判断对象是否是可迭代对象。
from collections import Iterable
a = open('input.txt')
b = 1
c = 'yin银鱼'
d = {'name':"yin银鱼"}
e = (1,2,3,4)
f = {1,2,3,4}
g = [1,2,3,4]
print(isinstance(a, Iterable)) # True
print(isinstance(b, Iterable)) # False
print(isinstance(c, Iterable)) # True
print(isinstance(d, Iterable)) # True
print(isinstance(e, Iterable)) # True
print(isinstance(f, Iterable)) # True
print(isinstance(g, Iterable)) # True
可以发现,文件对象、字符串、字典、元组、集合、列表都是可迭代对象。
其实,判断一个对象是否可迭代,关键看这个对象是否有__iter__()方法。
a = open('input.txt')
b = 1
c = 'yin银鱼'
d = {'name':"yin银鱼"}
e = (1,2,3,4)
f = {1,2,3,4}
g = [1,2,3,4]
print(hasattr(a, "__iter__")) # True
print(hasattr(b, "__iter__")) # False
print(hasattr(c, "__iter__")) # True
print(hasattr(d, "__iter__")) # True
print(hasattr(e, "__iter__")) # True
print(hasattr(f, "__iter__")) # True
print(hasattr(g, "__iter__")) # True
由此,可以得出结论:
凡是有__iter__()方法的对象都是可迭代对象(Iterable)
关于迭代器的理解
可迭代对象说完了,就可以说迭代器了。
说迭代器之前,我们还是再说说容器与可迭代对象:
在python中,属于容器类型有:list,dict,set,str,tuple…。容器仅仅只是用来存放数据的,它并不具备从容器中取出元素的功能,事实上,是可迭代对象赋予了容器这种取出元素的能力。(此处参考博文)
说了这么多,那么什么是迭代器呢?
廖大大给出的解释:可以被next()调用并不断返回下一个值的对象叫迭代器Iterator。
廖大大总结得很到位(抱紧大腿),迭代器与可迭代对象区别在于__next__()方法。
用代码验证一下:
from collections.abc import Iterator
a = open('input.txt')
b = 1
c = 'yin银鱼'
d = {'name':"yin银鱼"}
e = (1,2,3,4)
f = {1,2,3,4}
g = [1,2,3,4]
print(isinstance(a, Iterator)) # True
print(isinstance(b, Iterator)) # False
print(isinstance(c, Iterator)) # False
print(isinstance(d, Iterator)) # False
print(isinstance(e, Iterator)) # False
print(isinstance(f, Iterator)) # False
print(isinstance(g, Iterator)) # False
print(hasattr(a, "__next__")) # True
print(hasattr(b, "__next__")) # False
print(hasattr(c, "__next__")) # False
print(hasattr(d, "__next__")) # False
print(hasattr(e, "__next__")) # False
print(hasattr(f, "__next__")) # False
print(hasattr(g, "__next__")) # False
可以看出,只有文件类型的对象是迭代器。
为了更深入的理解迭代器,我们引入for循环内部机制。
我们前面分析过,可迭代对象是不可以直接从中取得元素,那for循环是如何取得可迭代对象中的元素呢?
my_list = [1,2,3,4,5]
for i in my_list:
print(i)
========输出结果========
1
2
3
4
5
其实在for循环内部,首先my_list会调用__iter__()方法,将列表my_list变为一个迭代器,然后这个迭代器再调用其__next__()方法,最后依次打印各个元素。
注意:可迭代对象转化为迭代器,可使用iter()方法。一旦使用iter()方法,就会调用可迭代对象内部的__iter__()方法,返回一个迭代器对象。
my_list = ["s","w","q"]
print(isinstance(my_list, Iterator)) #False
my_iter = iter(my_list) #将可迭代对象转化为迭代器
print(isinstance(my_iter, Iterator)) #True
print(hasattr(my_iter, '__next__')) #True
#被next()调用,并不断返回下一个值
print(next(my_iter)) # s
print(next(my_iter)) # w
print(next(my_iter)) # q
由此,我们就验证了:
迭代器Iterator就是可以被next()调用并不断返回下一个值的对象。
关于生成器的理解
在python中,一边循环一边计算的机制,称为生成器:generator
要创建一个generator,有很多种方法,这里我们介绍其中的两种方法:
第一种,方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator。
g = (x * x for x in range(10))
print(type(g)) #<class 'generator'>
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 4
第二种,一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
my_odd = odd()
print(next(my_odd))
print(next(my_odd))
print(next(my_odd))
=============输出结果=============
step 1
1
step 2
3
step 3
5
注意:函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
那么,生成器与迭代器有什么关系呢?
其实,生成器就是一种特殊的迭代器。不信的话,我们验证一下。
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
my_odd = odd()
g = (x * x for x in range(10))
print(hasattr(my_odd, "__next__")) # True
print(isinstance(my_odd, Iterator)) # True
print(hasattr(g, "__next__")) # True
print(isinstance(g, Iterator)) # True
总结
好了,我们来总结一下:
- 凡是有__iter__()方法的对象都是可迭代对象
- 凡是有__iter__()方法和__next__()方法的对象都是迭代器。
- 生成器是一种特殊的迭代器。
用一张图来描述就是: