python 可迭代对象、迭代器与生成器小结

这两天刚好学习了这一块的知识,就借机会整理下这三个概念的关系啦。
废话不多说,开始吧!

关于可迭代对象的理解

说迭代器之前,必须要说可迭代对象。
什么是可迭代对象?
顾名思义,就是一个对象能够被迭代的使用。
我们可以利用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

总结

好了,我们来总结一下:

  1. 凡是有__iter__()方法的对象都是可迭代对象
  2. 凡是有__iter__()方法和__next__()方法的对象都是迭代器。
  3. 生成器是一种特殊的迭代器。

用一张图来描述就是:
在这里插入图片描述

最后,再次感谢看雪大大廖大大
另外,欢迎大家指正和补充~

猜你喜欢

转载自blog.csdn.net/qq_42206477/article/details/84582701