迭代器和可迭代对象
由for循环的内部原理说起
list01 = [2,434,5,6,8]
for item in list01:
print(item)
大家有没有想过list类型对象为什么可以被for循环呢?
能够被for循环的条件是:它是可迭代对象(iterable)。
那么什么是可迭代对象呢?
参考一下内置函数iter()的官方说明文档:
iter(object[, sentinel])
返回一个 iterator对象。根据是否存在第二个实参,第一个实参的解释是非常不同的。如果没有第二个实参,object 必须是支持迭代协议(有 iter() 方法)的集合对象,或必须支持序列协议(有 getitem() 方法,且数字参数从 0 开始)。如果它不支持这些协议,会触发 TypeError。如果有第二个实参 sentinel,那么 object 必须是可调用的对象。这种情况下生成的迭代器,每次迭代调用它的 next() 方法时都会不带实参地调用 object;如果返回的结果是 sentinel 则触发 StopIteration,否则返回调用结果。
由此我们可以明确知道什么是可迭代的对象:即对象实现了能返回迭代器的 iter 方法,或对象实现了 getitem 方法,而且其参数是从零开始的索引。(两者都实现也行)
一文读懂Python可迭代对象、迭代器和生成器
判断一个对象是否是可迭代对象:
# 看对象是否具有__iter__方法 或 __getitem__方法.(dir()内置方法将生成一个列表囊括对象所有方法),若具有就说明
# 它是一个可迭代对象
>>> '__iter__' in dir([])
True
>>> '__getitem__' in dir([]) #还要进一步看其数字参数是否从 0 开始
True
>>>
情况一:实现了能返回迭代器的__iter__方法(迭代器是具有__next__()
方法的对象)。
class SkillIterator:
"""
迭代器
"""
def __init__(self, target):
self.target = target
self.index = 0
def __next__(self):
# 如果索引越界 则抛出StopIteration异常
if self.index > len(self.target) - 1:
raise StopIteration
# 返回下一个元素
item = self.target[self.index]
self.index += 1
return item
class SkillManager:
"""
可迭代对象
"""
def __init__(self, skills):
self.skills = skills
def __iter__(self):
# 创建迭代器对象 传递 需要迭代的数据
return SkillIterator(self.skills)
情况二:对象实现了 getitem 方法,而且其参数是从零开始的索引,python解释器会创建一个迭代器并尝试按顺序(从索引 0 开始)获取元素。
class Eter_a:
def __init__(self,text):
self.sub_text=text.split(" ")
def __getitem__(self, index):
return self.sub_text[index]
ob_a=Eter_a("Hello world!")
for i in ob_a:
print(i)
一般我们不用getitem去实现一个迭代器,所以重点还是放在 __iter__上好了。
补充一下:标准迭代器里面大部分都同时实现了__next__和__iter__方法。所以有人这样说:
内部含有'__iter__'并且含有"__next__"方法的对象,就是迭代器
for循环内部原理
实际执行情况如下图:
1)调用可迭代对象的__iter__方法返回一个迭代器对象(iterator)
2)不断调用迭代器的__next__方法返回元素
3)直到迭代完成后,处理StopIteration异常
根据以上条件,我们使用while来模拟一下for循环:
# 1. 获取迭代器对象
list01 = [2,434,5,6,8]
iterator = list01.__iter__() #(1)先调用__iter__方法获取一个迭代器
while True:
try:
item = iterator.__next__() # (2)不断调用迭代器的__next__方法
print(item)
except StopIteration: #(3)直到捕获StopIteration异常停止迭代
break # (4)跳出循环体
,
生成器generator
(首先它是一个函数,特殊之处在于函数使用yield返回,这样在调用此函数时会返回一个生成器对象,这是一个函数到对象的神奇过程,体现了一种惰性返回的思维(即用到才返回,不用不返回,而不是管你用不用哪怕只用一部分也一次性全部返回return)。生成器是迭代器的一种,明白了吧,想更深入理解就往下看)
生成器及其作用
python 生成器和迭代器有这篇就够了
通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator
生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器。
生成器:generator
1.定义:能够动态(循环一次计算一次返回一次)提供数据的可迭代对象。
2.作用:在循环过程中,按照某种算法推算数据,不必创建容器存储完整的结果,从而节省内存空间。数据量越大,优势越明显。
3.以上作用也称之为延迟操作或惰性操作,通俗的讲就是在需要的时候才计算结果,而不是一次构建出所有结果。
生成器函数
1.定义:含有yield语句的函数,返回值为生成器对象
。
2. 创建语法
def 函数名():
…
yield 数据
…
– 调用:
for 变量名 in 函数名():
语句
3.说明:
- 调用生成器函数将返回一个生成器对象,不执行函数体。
- yield翻译为”产生”或”生成”
4.执行过程:
(1) 调用生成器函数会自动创建迭代器对象。
(2) 调用迭代器对象的__next__()方法时才执行生成器函数。
(3) 每次执行到yield语句时返回数据,暂时离开。
(4) 待下次调用__next__()方法时继续从离开处继续执行。
5.原理:生成迭代器对象的大致规则如下
- 将yield关键字以前的代码放在next方法中。
- 将yield关键字后面的数据作为next方法的返回值。
(qz理解:或者说当内部调用next方法时,会跳到yield关键字之前的代码继续执行,并且将yield关键字后面的数据作为next方法的返回值,因为调试的话执行next会跳到yield关键字之前的代码。)
内置生成器
1,枚举函数enumerate
1).语法:
for 变量 in enumerate(可迭代对象):
语句
for 索引, 元素in enumerate(可迭代对象):
语句
2).作用:遍历可迭代对象时,可以将索引与元素组合为一个元组。
2,zip函数
1).语法:
for item in zip(可迭代对象1, 可迭代对象2….):
语句
2).作用:将多个可迭代对象中对应的元素组合成一个个元组,生成的元组个数由最小的可迭代对象决定。
生成器表达式
1.定义:用推导式形式创建生成器对象。
2.语法:变量 = ( 表达式 for 变量 in 可迭代对象 [if 真值表达式] )
(注意:可不要误解是元组推导式,不存在元组推导式,因为元组是不可变序列)
>>> (i for i in range(1,10))
<generator object <genexpr> at 0x0000023A9DDA7408>
>>> [i for i in range(1,10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>