Python 的可迭代对象、迭代器和生成器

迭代器和可迭代对象

由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]
>>>
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值