迭代器与生成器总结

Hello,大家好。本期来和大家一起学习一下迭代器和生成器的相关知识。

可迭代对象

想要学习迭代器,首先我们要知道什么是可迭代对象(iterable)。

概念

凡是内置有__iter__方法的对象都称之为可迭代对象。

可迭代对象判断

数值类型

不是可迭代对象。

a = 1
print(a.__iter__)
# AttributeError: 'int' object has no attribute '__iter__'

字符串类型

是可迭代对象。

c = 'hello'
print(c.__iter__())
print(c.__next__())
# <str_iterator object at 0x000002B860ECFFD0>
# AttributeError: 'str' object has no attribute '__next__'

列表类型

是可迭代对象。

d = ['a','c']
print(d.__iter__())
print(d.__next__())
# <list_iterator object at 0x00000238A05BFFD0>
# AttributeError: 'list' object has no attribute '__next__'

集合类型

是可迭代对象。

g = {1,2,3}
print(g.__iter__())
print(g.__next__())
# <set_iterator object at 0x0000025A8EB796C0>
# AttributeError: 'set' object has no attribute '__next__'

元组类型

是可迭代对象。

t = ('a','b','c')
print(t.__iter__())
print(t.__next__())
# <tuple_iterator object at 0x000001FE4ADBFFD0>
# AttributeError: 'tuple' object has no attribute '__next__'

字典类型

是可迭代对象。

dic = {'x':1,'y':2,'z':3}
print(dic.__iter__())
print(dic.__next__())
# <dict_keyiterator object at 0x000001F7FF95A4F0>
# AttributeError: 'dict' object has no attribute '__next__'

由此我们可以发现,只要是容器都有此__iter__方法。

迭代器

概念

执行可迭代对象下的__iter__方法,返回的值就是一个迭代器。

示例:

dic = {'x': 1, 'y': 2, 'z': 3}  # 可迭代对象
iter_dic = dic.__iter__()  # 可迭代对象变成迭代器
print(iter_dic)  # <dict_keyiterator object at 0x0000019387512270>

print(iter_dic.__next__())  # x
print(iter_dic.__next__())  # y
print(iter_dic.__next__())  # z
print(iter_dic.__next__())  # 报错 StopIteration 应该被当成一种结束信号,代表迭代器取干净了

作用

迭代器提供了一种通用的且不依赖于索引的迭代取值方式的功能。

迭代器与可迭代对象的区别

可迭代对象:

一定有__iter__()方法,不一定有__next__()方法。


迭代器:

  • 既内置有__iter__()方法,迭代器执行__iter__()方法得到的仍然是迭代器本身。

  • 又内置有__next__()方法,迭代器执行__next__()方法可以不依赖索引取值。


注意:迭代器一定是可迭代的对象,而可迭代的对象却不一定是迭代器

误区

迭代器是一个容器,当取完其中的值后,就不能继续了,否则会报错。

# 01
l = [1, 2, 3]
print(l.__iter__().__next__())  # 1
print(l.__iter__().__next__())  # 1   此时生成了一个新的迭代器,又基于新的取值
# 02
l = [1, 2, 3]
print(l.__iter__().__next__())  # 1
iter_l = l.__iter__()
print(iter_l.__next__())  # 1    此时生成了一个新的迭代器,又基于新的取值
print(iter_l.__next__())  # 2
print(iter_l.__next__())  # 3

print(iter_l is iter_l.__iter__().__iter__().__iter__())  # True

for循环

本质

有没有一种好的方法?

  • 1.自动把可迭代对象变成迭代器。

  • 2.自动获取迭代器next的值。

  • 3.next最后不报错。

因此for本质应该称之为迭代器循环。

运行机制

第一步:先调用in后面那个对象的__iter__()方法,将其变成一个迭代器.

  • 如果是 迭代器 iter()可以变成迭代器 ——老的迭代器

  • 如果是 可迭代对象 iter()可以变成迭代器 ——新的迭代器

第二步:调用next(迭代器),将得到的返回值赋值给变量名 i.

第三步:循环往复直到next(迭代器)抛出异常,for会自动捕捉异常StopIteration然后结束循环.

示例:

l = [1, 2, 3]
iter = l.__iter__()

for k in iter:
    print(k)

print(iter.__iter__().__next__()) #基于老的迭代器,值已经取完,所以报错。
'''
1
2
3
StopIteration
'''

迭代器总结

优点:

  1. 提供一种通用的且不依赖于索引的迭代取值方式

  1. 同一时刻在内存中只存在一个值,更节省内存

缺点:

  1. 取值不如按照索引的方式灵活,(不能取指定的某一个值,而且只能往后取)

  1. 无法预测迭代器的长度

生成器

概念

生成器就是一种自定义的迭代器,本质就是迭代器。

特点:

  • 但凡函数内包含yield关键字,调用函数时不会执行函数体代码。

  • 调用函数时会得到一个返回值,该返回值就是生成器对象。

示例:

def func():
    print('======1')
    yield 1
    print('======2')
    yield 2
    print('======3')


g = func()
g.__next__()  # g 既有__iter__()方法,又有__next()方法
print(g)  # 生成器,本质就是迭代器<generator object func at 0x000001A50359DF20>
print(g is g.__iter__().__iter__())

# 会触发函数的执行,直到碰到一个yield停下来,并且将yield后的值当作本次next的结果返回
print(g.__next__())
print(g.__next__())
'''
======1
<generator object func at 0x000001F6B7876C10>
True
======2
2
StopIteration
'''

yield总结

1.yield提供了一种自定义迭代器的解决方案。

2.yield可以保存函数的暂停的状态。

3.yield只能在函数内使用。

yield与return的异同

1.相同点:都可以返回值,值的类型与个数都没有限制。

2.不同点:yield可以返回多次值(暂停),而return只能返回一次值函数就结束了(结束)。

生成器计算斐波拉契数列

'''
定义一个生成器,这个生成器可以生成10位斐波拉契数列,得到斐波拉契数列
斐波那契数列:数列中每一个数的值都等于前两个数相加的值 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55.........])
'''


def run(n):
    i, a, b = 0, 1, 1
    while i < n:
        yield a  # 第一是a 1  b 1 第二次 a 1  b 2  第三次 a 2  b 3 第四次 a 3 b 5 第五次 a 5 b 8
        a, b = b, a + b
        i += 1


my_run = run(10)
print(my_run)  # <generator object run at 0x000001B9BACC24A0>
print(list(my_run))  # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 相当与把 my_run里面的值取完了

print('自定义迭代器(生成器)的for循环')
for i in my_run:
    print(i)  # 基于老的迭代器,没有数据可取
for i in my_run:
    print(i)  # 基于老的迭代器,没有数据可取
'''
<generator object run at 0x00000287FFFC9F90>
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
自定义迭代器(生成器)的for循环
'''

生成器.send()传值

了解 即可。

# 千与千寻
# 函数可不可以不断传值?
# yield的表达式形式的应用: x=yield
def run1(name):
    print('千与千寻%s准备开吃' % name)
    food_list = []
    while True:
        food = yield food_list
        print('%s吃了%s' % (name, food))
        food_list.append(food)


g = run1('无脸男')
print(g)  # <generator object run1 at 0x000002DB9AB824A0>
res1 = next(g)  # 千与千寻无脸男准备开吃
print(res1)  # []

res2 = g.send('黄金')  # 不仅仅把数据送进去赋值给food 还执行了next方法
print(res2)  # ['黄金']  返回的是 yield 后面的数据

res3 = g.send('黄金')  # 不仅仅把数据送进去赋值给food 还执行了next方法
print(res3)  # ['黄金', '黄金']

res3 = g.send('黄金')  # 不仅仅把数据送进去赋值给food 还执行了next方法
print(res3)  # ['黄金', '黄金', '黄金']

res3 = g.send('11111黄金')  # 不仅仅把数据送进去赋值给food 还执行了next方法
print(res3)  # ['黄金', '黄金', '黄金', '11111黄金']

生成器计算阶乘

'''
用生成器来计算1!+2!+3!+4!+......10!的和
'''


# 阶层
# 1! = 1

# 2! = 2*1

# 3! = 3*2*1

# 4! = 4*3*2*1

# 计数变量i

def func(n):  # n 的阶层
    i = 1  # 计数的变量 第一次  i  1
    j = 1
    while i <= n:
        yield j  # 第一次  j  1
        i += 1  # 计数自增1 第二次  i  2 第三次   i  3 第四次   i  4  第5 次  i 5
        j = j * i  # 第二次  j  2 第三次  j   1*2 * 3 = 6 第四次   j  1 *2 * 3 * 4 = 24 第5 次 1 *2 * 3 * 4 * 5


a = func(10)
print(a)  # <generator object func at 0x0000018159932510>
print(list(a))  # [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
print(sum(list(a))) # 0 # 基于老的迭代器,没有数据可取

更多用法,欢迎小伙伴后台留言哦。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值