迭代器和生成器(面试重点,必会!!!)

一. 列表生成式:

a1 = [x for x in range(1,10)]
a2 = [x**2 for x in range(1,10)]

也可以放一个函数:

def f(n):
    return n**3

a3 = [f(x) for x in range(1,10)]

二. 生成器:

背景:通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
  所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator
  
定义: 在Python中,这种一边循环一边计算的机制,称为生成器:generator 。

说明:生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

两种创建方式:
  1. (x*2 for x in range(5,100)) # 此处注意区分列表生成式
  2. yield

其中:

  • 生成器表达式:返回一个对象,这个对象只有在需要的时候才产生结果
  • 生成器函数:也是用def定义的,利用关键字yield(相当于return)一次性返回一个结果,阻塞,重新开始

为了节省空间,生成器生成的数据并没有放到内存中,只有在调取的时候才往内存中放。
只能从前往后逐一调取。

next() 返回迭代器的下一个项目

a = (x*2 for x in range(5,100))   #元组
print(next(a))  #将5返回给x*2    10

用生成器来实现斐波那契数列的例子是:

def fib(max):
    a, b = 0, 1
    while max > 0:
        a, b = b, a + b
        yield a
        max -= 1

print([i for i in fib(10)])

f.send() 和 next() 的区别:

  • f.send(),将参数传给yield前一个变量,也就是说send()可以强行修改上一个yield的值;
  • next()参数是迭代器函数名字;
  • 第一个send()不可传值,写作f.send(None),相当于next(b)。

三. 迭代器

定义:任何实现了__iter__和__next__()方法的对象都是迭代器(这是迭代器协议),__iter__返回迭代器自身,__next__返回容器中的下一个值,直到容器中没有更多元素时抛出StopIteration异常并停止迭代。

迭代就是循环,它是一个带状态的对象,能在你调用next()方法的时候返回容器中的下一个值。
迭代器就像一个懒加载的工厂,等到有人需要的时候,它才会生成值并返回,没调用的时候就处于休眠状态等待下一次调用。

生成器与迭代器的区别
  • 迭代器(iterator)是一个实现了迭代器协议(__iter__和__next__()方法)的对象。
  • 生成器(generator)是通过yield语句或生成器函数快速生成迭代器,可以不用iter和next方法 。
  • 生成器本质上也是一个迭代器,自己实现了可迭代协议,与迭代器不同的是生成器的实现方式不同,可以通过生成器表达式和生成器函数两种方式实现,代码更简洁。
  • 生成器和迭代器都是惰性可迭代对象,只能遍历一次,数据取完抛出Stopiteration异常

四. 可迭代对象

我们已经知道,可以直接作用于for循环的数据类型有以下几种:

  • 一类是集合数据类型,如list,tuple,dict,set,str等
  • 一类是generator,包括生成器表达式和生成器函数

定义:实现了__iter__方法的对象就叫做可迭代对象。说白了就是可以直接作用于for 循环的对象统称为可迭代对象:Iterable

区别:一个可迭代的对象必须是定义了__iter__()方法的对象;而一个迭代器必须是同时定义了__iter__()方法和next()方法的对象。

for函数的本质:在python3中,就是不断调用next() 方法实现for循环。

x = [1,2,3]
for i in x:
    print(i)

实际执行情况:
在这里插入图片描述
等价于:

# 首先获得Iterator对象:
iterator = iter([1, 2, 3])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(iterator)
        print(x)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

总结:

  • 凡是可作用于for循环的对象都是可迭代对象;
  • 凡是可作用于next()函数的对象都是迭代器,它们表示一个惰性计算的序列;
  • 集合数据类型如list、dict、str等是可迭代对象,但不是迭代器,不过可以通过iter()方法获得一个Iterator对象;
  • 文件是可迭代对象,也是迭代器。
你可能会问,为什么list、dict、str等数据类型不是迭代器?

这是因为Python的迭代器对象表示的是一个数据流,可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
迭代器甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
list,str,dict…这些可迭代的对象你可以随意的读取所以非常方便易用,但是你必须把它们的值放到内存里,当它们有很多值时就会消耗太多的内存。

如何判断是可迭代对象还是迭代器?如何把可迭代对象转换为迭代器?
from collections import Iterator  # 迭代器
from collections import Iterable  # 可迭代对象

s = 'hello'

print(isinstance(s, Iterator))  # 判断是不是迭代器
print(isinstance(s, Iterable))  # 判断是不是可迭代对象

print(isinstance(iter(s), Iterator))  # 把可迭代对象转换为迭代器 iter(s)
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值