0 总述
主要介绍可迭代对象(iterable)、迭代器(iterator)、生成器(generator)。
迭代 是python 最强大的功能之一,是访问集合元素的一种方法。
迭代的概念
迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
1 可迭代对象(iterable)
1.1 定义介绍
- 在
python 中, 某对象实现__iter__() 方法或者实现__getitem__() 方法而且其参数从0开始索引,那么该对象就是可迭代对象 。定义可迭代对象时必须实现__iter__() 方法或者__getitem__() 方法。 - 使用
iter() 方法将可迭代对象 变成迭代器 。 - 通俗的说就是可以用
for 循环的对象都是可迭代对象。例如:list、dict、tuple、set、string。其实都是序列,所以说任何序列都是可迭代的对象, 其原因在于他们至少都会实现__getittem__() 方法。(序列都可以通过索引获取元素) - 是一种通俗的叫法,并不是指某种具体的数据类型。
- 迭代器有一种具体的迭代器类型,比如
list_iterator ,set_iterator 。可迭代对象实现了__iter__ 方法,该方法返回一个迭代器对象。
1.2 对可迭代对象进行迭代操作的具体过程
- 调用可迭代对象的
__iter__() 方法,返回一个迭代器对象 - 调用迭代器的
__next__() 方法返回每一次迭代的内容,直到迭代完成后,抛出stopiteration异常 。 可迭代对象 之所以能迭代,是因为实现了__iter__() 方法。当使用for 循环时候,解释器会检查对象是否有__iter__ () 方法,有的话就是调用它来获取一个迭代器。所以没有__iter__ () 方法但实现了__getitem__ () ,解释器会创建一个迭代器 ,尝试从0开始按顺序遍历元素。如果尝试失败,Python 便会抛出TypeError 错误。
所以说虽然字符串、列表、元组、字典、集合等均不是迭代器,但是他们可以在for 中进行循环的原因是他们是可迭代对象 ,在进行迭代操作中会有__iter__() 返回对应的迭代器 ,本质上就是对调用__iter__() 后得到的迭代器 通过不断使用next() 函数实现的,例如:下面举例说明:
list_data = [1,2,3]
list_iterator = iter(list_data)
str_data = "1,2,3"
str_iterator = iter(str_data)
tuple_data = (1,2,3)
tuple_iterator = iter(tuple_data)
dict_data = {"1":1,"2":2,"3":3}
dict_iterator = iter(dict_data)
print(type(list_iterator))
print(type(str_iterator))
print(type(tuple_iterator))
print(type(dict_iterator))
使用while 循环模拟for...in 的作用
list1 = [1, 2, 3, 4, 5]
iterator = iter(list1)
iterator = list1.__iter__()
while True:
try:
print(iterator.__next__(), end = " ")
except StopIteration as e:
break
1.3 创建一个可迭代对象
class Vector(object):
def __init__(self,components):
self.components = list(components)
def __iter__(self):
return iter(self.components)
V1 = Vector([1,2,3])
for i in V1:
print(i)
上面的例子中实现了 __iter__() 方法,解释器可以从类对象中重复地取出元素并打印。
1.4 判断是否是可迭代对象
如果要检查某一个对象是否为可迭代对象,其实可以使用isinstance() 函数,该函数用于判断对象是否为某一类型。
from collections import Iterable
print(isinstance([1,2,3],Iterable))
from collections.abc import Iterable
print(isinstance([1,2,3],Iterable))
from typing import Iterable
print(isinstance([1,2,3], Iterable))
举例说明:
class Vector(object):
def __init__(self,components):
self.components = list(components)
def __iter__(self):
return iter(self.components)
vector_data = Vector([1,2,3])
vector_iterator = iter(vector_data)
list_data = [1,2,3]
list_iterator = iter(list_data)
from typing import Iterable
from typing import Iterator
print("使用typing库Iterable和Iterator")
print("vector_data :是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_data, Iterable),isinstance(vector_data, Iterator)))
print("vector_iterator:是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_iterator, Iterable),isinstance(vector_iterator, Iterator)))
print("list_data :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_data, Iterable),isinstance(list_data, Iterator)))
print("list_iterator :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_iterator, Iterable),isinstance(list_iterator, Iterator)))
from collections.abc import Iterable
from collections.abc import Iterator
print("使用collections.abc库Iterable和Iterator")
print("vector_data :是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_data, Iterable),isinstance(vector_data, Iterator)))
print("vector_iterator:是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_iterator, Iterable),isinstance(vector_iterator, Iterator)))
print("list_data :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_data, Iterable),isinstance(list_data, Iterator)))
print("list_iterator :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_iterator, Iterable),isinstance(list_iterator, Iterator)))
注意点
如果可迭代对象只是实现了__getitem__() 的话,abc.Iterable 是不考虑该方法的,这便导致了isinstance() 判断不准确。更准确的方法应该是尝试调用iter() 函数,如果该对象不可迭代,就会抛出TypeError 的错误。
from collections.abc import Iterable
class Vector(object):
def __init__(self,components):
self.components = list(components)
def __getitem__(self,index):
return self.components[index]
V1 = Vector([1,2,3])
print(isinstance(V1,Iterable))
V1 = iter(V1)
print(isinstance(V1,Iterable))
2 迭代器(iterator)
迭代器 可以记住遍历对象的位置。其内部有一个状态用于记录迭代所在的位置,以便下次迭代时候能取出正确的元素迭代器 有两个基本方法iter() 、next() 。使用iter() 方法将可迭代对象 变成迭代器 。使用next() 方法返回下一个值。定义迭代器 ,必须实现__iter__() 和__next__() 方法。(Python 2 里是需要类内有next() 方法)- 从集合的第一个元素开始访问,直到所有的元素被访问完结束。
迭代器 只能往前不会后退。 字符串 ,列表 或元组对象 都可用于创建迭代器 。迭代器 一定是可迭代对象,但是可迭代对象 不一定是迭代器 。例如:字符串、字典、元组、集合等。迭代器 就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。
2.1 创建一个迭代器
next() 方法调用到末尾的时候会跳出StopIteration ,for 循环等就是在此时终止循环的
list_data = [1,2,3]
interator_data = iter(list_data)
print(interator_data)
print(type(interator_data))
print(next(interator_data))
print(next(interator_data))
print(next(interator_data))
print(next(interator_data))
2.2 判断是否是迭代器
可以参考1.4
2.3 使用for 进行迭代器的使用
list_data = [1,2,3]
interator_data = iter(list_data)
print(interator_data)
for i in interator_data:
print(i,end=" ")
2.4 使用next() 进行迭代器的使用
import sys
interator_data = iter([1,2,3])
while True:
try:
print (next(interator_data), end=" ")
except StopIteration:
sys.exit()
2.5 可迭代对象与迭代器的区别
迭代器 一定是可迭代对象 ,但是可迭代对象 不一定是迭代器 。例如:字符串 、字典 、元组 、集合 等。
迭代器 可以使用next() 方法,但是可迭代对象 不一定可以使用next() 方法。
一般可以使用iter() 方法将可迭代对象 变成迭代器 。
list_data = [1,2,3]
interator_data = iter(list_data)
print(interator_data)
print(type(interator_data))
print(next(interator_data))
print(type(list_data))
print(next(list_data))
2.5 创建一个迭代器
定义一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 。
类中成员函数__iter__() 在外部对类的示例化变量调用iter() 方法的时候会被调用,主要功能是实现返回一个特殊的迭代器对象(一般就是自己),(一般也会再次或者在__init__() 中设置迭代器的七点)。
类内成员函数__next__() 在外部对类的示例化变量调用next() 方法的时候会被调用,会返回迭代器 中下一个需要被迭代出的值,一般中间应该有StopIteration 异常,对迭代 的完成进行标识控制。
创建一个返回数字的迭代器,初始值为 1,逐步递增 1:
class My_interator:
def __iter__(self):
self.start_number = 1
return self
def __next__(self):
if self.start_number <= 10:
x = self.start_number
self.start_number += 1
return x
else:
raise StopIteration
interator_class = My_interator()
interator_data = iter(interator_class)
for x in interator_data:
print(x,end = " ")
3 生成器(generator)
3.1 定义介绍
- 在
Python 中,使用了yield 的函数被称为生成器函数generator function 。调用一个生成器函数 ,返回的是一个实例化迭代器对象 。 - 跟普通函数不同的是,
生成器函数 是一个返回生成器 的函数,只能用于迭代操作,更简单点理解生成器 就是一种特殊的迭代器 。生成器 自动实现了“迭代器协议”(即__iter__() 和__next__() 方法),不需要再手动实现两方法。 生成器 在迭代的过程中可以改变当前迭代值,而修改普通迭代器 的当前迭代值往往会发生异常,影响程序的执行。(因为生成器 有send() 方法)- 在调用
生成器 运行的过程中,每次遇到yield 时函数会暂停并保存当前所有的运行信息,返回yield 语句表达式的值, 并在下一次执行next() 或send() 方法时从当前位置继续运行。 - 列表对内存的开销比较大。如果列表元素可以按照某种算法推算出后面的元素,这样就不必创建完整的列表。在
python 中我们把一边循环一边计算的机制,即为生成器generator 。
3.2 创建生成器的例子
def myList(num):
now = 0
while now < num:
val = (yield now)
now = now + 1 if val is None else val
my_list = myList(5)
print my_list.next()
print my_list.next()
my_list.send(3)
print my_list.next()
3.3 判断是否是生成器函数或生成器
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
from inspect import isgeneratorfunction
print(isgeneratorfunction(fab))
print(isgeneratorfunction(fab(5)))
import types
print(isinstance(fab, types.GeneratorType) )
print(isinstance(fab(5), types.GeneratorType))
3.5 关于更多举例说明
参考专门讲解生成器运行原理和yield 关键字的文章。
python中yield的用法
3.6 生成器表达式(generator expression)
生成器表达式 是列表推导式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象 而不是列表对象 。
a = (x*x for x in range(10))
print(type(a))
print(next(a))
print(next(a))
print(sum(a))
LAST 参考文献
python中可迭代对象、迭代器、生成器等都有什么区别 - 华为云
Python3.9中,使用from collections import Iterable报错_qq_39321623的博客-CSDN博客
Python3 迭代器与生成器 | 菜鸟教程
python中可迭代对象、迭代器、生成器等都有什么区别 - 华为云
黄哥Python:如何判断一个对象是生成器还是迭代器? - 知乎
理解Python可迭代对象、迭代器、生成器 - stardsd - 博客园
跟你深入剖析可迭代对象和迭代器的区别与联系 - SegmentFault 思否
python中yield的用法_呆呆象呆呆的博客-CSDN博客
理解Python可迭代对象、迭代器、生成器 - stardsd - 博客园
Python可迭代对象,迭代器,生成器的区别_Nxin的小抄本-CSDN博客
可迭代对象、迭代器和生成器 - 华为云
(1 封私信 / 82 条消息) 如何更好地理解Python迭代器和生成器? - 知乎
|