Python可迭代对象、迭代器对象以及生成器

## 要点
什么是迭代?
for循环的实现原理
迭代的设计思想是什么?
生成器的作用
生成器函数的使用过程
生成器表达式与内置生成器

什么是迭代?

  迭代就是循环,也叫遍历,每一次迭代得到结果都会作为下一次迭代的初始值,在Python中常见的就是支持for循环的内建数据类型,如列表,字符串等
  在迭代的过程中,出现可迭代对象和迭代器对象,注意:这是两个不同的对象,可迭代对象继承于Iterable类,并重写Iterable类中的方法__iter__方法;迭代器对象继承于Iterator类,并重写了Iterator类中的__next__方法。

Python中for循环的原理

  通常情况下,我们在Python中使用for循环时,一般都会有一个遍历的数据类型,并从这个数据类型中依次拿出数据进行使用。如以下的代码:

list01 = [32, 4, 4, 5, 6, 7, 8]
for item in list01:
    print(item)

  这个代码大家是再熟悉不过的,通过for…in…遍历列表,就可以依次获取列表中的数值。那么问题来了,这个列表是什么?有什么特点?怎么就能通过for遍历获取数据?
  假设我们把它作为一个可迭代对象进行操作,看能否拿到这些数据?

iterator = list01.__iter__()
while True:
    try:
        item = iterator.__next__() 
        print(item)
    except StopIteration:
        break

  我们先对列表调用Iterable类中的__iter__()方法,获得一个迭代器对象,而从迭代器的定义中看,迭代器是提供一种方法顺序访问一个聚合对象中各个元素;因此,在这里迭代器对象通过调用Itertor类中的__next_()方法开始对迭代器逐个读取,直到读取完毕,抛出StopIteration()异常。
  其实,像List、str等Python的内建数据结构已经预先在定义结构时使用__iter__方法实现了迭代器,而for循环的使用,其核心逻辑就是不断重复获取下一个元素,直到没有元素。同时,也可以看出,for循环实现的必要条件就是实现循环或者遍历的对象必须是可以获取迭代器对象的。

迭代的设计思想

  刚刚介绍到都会在使用Iterable类和Iterator类的方法,实现迭代,那么在我们进行自定义可迭代类和自定义迭代器类时,我们又该如何去处理呢?没错,在这里我们需要通过继承Iterable类和Iterator类,分别重写__iter__方法和__next__方法,以实现我们在通过for调用时,做到可以通过__iter__方法生成迭代器对象,并调用自定义迭代器类中重写的__next__方法,以此来达到迭代的效果。可以先看一下下面的代码。

# 迭代器类
class NumIterator:
    def __init__(self, data):
        self.__data = data
        self.__index = -1
    def __next__(self):
        try:
            self.__index += 1
            return self.__data[self.__index]
        except IndexError:
            raise StopIteration()
# 可迭代对象类                        
class MyRange:
    def __init__(self, num):
        self.__num_list = []
        count = 0
        while count < num:
            self.__num_list.append(count)
            count += 1

    def __iter__(self):
        return NumIterator(self.__num_list)
# 测试代码        
for number in MyRange(5):
    print(number)

  从这段代码中,我们可以看到,通过自定义一个MyRange类,并实例化对象;这个方法通过构造方法中的逻辑代码形成属性列表;最重要的是,通过调用__iter__方法进行生成自定义的迭代器对象,同时,将列表作为参数传递给迭代器对象,由此一来,自定义的可迭代类就完成了其本身所需要实现的功能。接下来,通过迭代器实例对象执行其__next__方法,并且通过索引完成对数据的一个个读取。在遍历结束时,抛出stopIteration()异常。
  以上,这段代码很好地完成了对range()函数的原理实现。同时,在这个过程中,for循环在做什么?真的是在单纯地直接执行MyRange类吗?其实并不然,从面向对象的角度来看,for循环其实是在访问Iterator类,这也是面向对象设计原则中要求,而我们所创建的自定义类其实都是在继承其这两个类,通过继承的方式,进行调用,以此达到要求。这也是我们常提起的迭代器设计模式。
在这里插入图片描述

生成器的作用

  生成器在循环过程中,由于是动态的提供数据的可迭代对象,因此可以推算数据,不需要通过创建容器来存储完整的结果,之后再做计算;而是通过循环一次,计算一次,返回一次,即在需要的时候计算结果并返回,而不是一次性的直接构建。这样的话,可以很好地节省内存空间,尤其是在数据量很大的时候,其生成器的优势也会更加明显。这种作用在计算机中也称为惰性操作或者叫做延迟操作。

生成器函数的使用过程

  生成器能够动态(循环一次、计算一次、返回一次)的提供数据的可迭代对象。也可以理解为特殊的迭代器,因为自定义的迭代器类需要手动的去实现__next__方法并进行逻辑处理。相对较为麻烦,因此,Python便给我们提供了简单快捷的一种方式:通过使用yield把__next__方法的执行逻辑提取出来,通过yield返回结果。

def my_range(stop_value):
    start = 0
    while start < stop_value:
        yield start
        start += 1

for number in my_range(5):
    print(number)  # 0 1 2 3 4

同样地,我们可以看一下他的执行过程:
(1)调用迭代器函数会自动创建迭代器对象;
(2)调用迭代器对象的__next__方法时才执行迭代器函数;
(3)每次执行到yied语句时返回数据,并且暂时离开;
(4)待下次调用__next__方法时才会从离开的地方继续执行。
需要注意的是:
(1)yield前面的代码内容就是抽取到__next__方法中的执行逻辑;而其后面的则是作为__next__方法的返回值。
(2)yield返回的是多个值,而return返回的是单个值

生成器表达式与内置生成器

生成器表达式是以推导式形式创建生成器对象
语法格式:

变量 = ( 表达式 for 变量 in 可迭代对象 [if 真值表达式] )

Python中常有的内置生成器
(1)enumerate枚举函数
语法:

for 变量 in enumerate(可迭代对象):
    语句

或者

for 索引, 元素 in enumerate(可迭代对象):
    语句

作用是遍历可迭代对象时,可以将索引与元素何为一个元组
(2)zip组合函数

语法:

for item in zip(可迭代对象1, 可迭代对象2.):
    语句

  作用是将多个可迭代对象中对应的元素组合成一个个元组,生成的元组个数由最小的可迭代对象决定。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值