迭代器,生成器
可迭代对象
【1】什么叫迭代?
迭代其实就是更新换代,每一次迭代的过程都需要依赖上一次的结果
例如
# 属于迭代,每次结果都依据上一次结果
# 例一
n = 1
while True:
print(n)
n +=1
# 例二
l1 = [1,2,3,4,5,6,7,8,9]
n = 0
while n < len(l1):
print(l1[n])
n += 1
【2】什么是可迭代对象
1.内置有__iter__方法的都可以称为可迭代对象
2.__iter__该类型代码的读法>>>:双下iter方法
【3】哪些数据是可迭代对象
依次列举并尝试调用双下iter方法即可
可迭代(支持for循环):字符串(str),字典(dict),元组(tuple),列表(list),集合(set)
不可迭代:整型(int), 浮点型(float),布尔型(bool)
迭代器对象
【1】什么是迭代器对象
可迭代对象调用双下iter方法之后生成的结果就是迭代器对象
【2】迭代器对象的特征
含有双下iter方法又含有双下next方法
含有__iter__又含有__next__
s=[1,2,3,4,5,6,7,8,9,10]
res = s.__iter__()
print(res) # <list_iterator object at 0x000001BAE2829780>
'''
迭代器对象 iterator object
'''
pice = res.__next__()
print(pice)
# 1 输出一次 依次弹出列表中的一个元素
【3】如何理解迭代器对象
迭代器对象能够极大的节省存储空间
- eq:类似于哆啦A梦的口袋,不用就是一个口袋面积,用的时候可以从中取出很多数据
【4】迭代器对象如何取值
调用双下next方法即可,如果取完了就会报错
【5】扩充了解
1.有很多双下方法其实都有简便写法,但不是全部
'''
__方法名__ 等价 方法名()
最常见的两个是
__iter__ iter()
__next__ next()
'''
2.有一些可迭代对象本身也是迭代器对象:>>> 文件对象
3.可迭代对象调用一次双下iter方法编程迭代器对象,如果继续调用,结果还是迭代器对象本身
for循环内部原理
'''
for循环底层原理
for 变量名 in 可迭代对象:
循环体代码
1.会将in后面的数据调用__iter__()变成迭代器对象
所以就解释了为什么文件对象也可以for循环,因为本身就是迭代器对象,再次调用不变
2.针对产生的迭代器对象依次调用__next__()方法迭代取值
3.当取值完之后,会自动处理报错并退出循环
'''
user_dict = {'name':'silence','age':18,'password':123}
# 1.先转换成迭代器对象
# res= user_dict.__iter__()
res = iter(user_dict)
# 2. 迭代取值
while True:
# print(res.__next__())
try:
print(next(res))
except Exception as e:
# 结束while循环
break
# name
# age
# password
迭代取值与索引取值的对比
1.索引取值
1.优势:
- 可以反复获取相同元素,并没有固定的方向
2.劣势:
- 只能支持有序的容器类型,无序的无法取值兼容性没有迭代取值高
2.迭代取值
1.优势:
- 兼容所有的容器类型
- 不使用索引进行取值
- 字典是不支持索引取值的
- 取到这个值的时候就会保存到当前的状态,下一次从这个位置开始向下取值
2.劣势:
- 取值的顺序永远都是从左往右,并且无法重复获取,取完就完了
- 除非取值取到尽头,否则永远不知道终止索引在哪里
- 迭代同一个对象只能重新创建
- 调用 next 取值的前提是 已经生成并且得到了一个迭代器对象
生成器对象(自定义迭代器)
def index():
print('你还记得我嘛')
# yield 123
yield 123,111
# 没有调用之前就是一个普通的函数
'''
当函数体代码中含有yield关键字
第一次调用函数并不会执行函数体代码
而是将函数变成了生成器
'''
# print(index) # <function index at 0x000001FF8B493E20>
# 加括号调用并接收结果:不执行代码,而是变成生成器对象(迭代器)
res = index()
# print(res) #<generator object index at 0x00000204F3AE6960> 变成生成器对象
# 变成生成器之后调用__next__就会开始执行函数体代码
# print(next(res)) #123 yield有点像return功能
print(next(res)) #(123, 111)
'''
生成器对象也是节省存储空间的 特性与迭代器对象一致
'''
def index():
print('你还记得我嘛')
yield 123
print('是不是忘记我了')
yield 123,111
'''
如果函数体代码中含有多个yield关键字,每执行一次__next__可以返回后面的值,并且让代码停留在yield位置
再次执行__next__基于上次的位置往后执行到下一个yield关键字处
如果没有了,再执行就会报错 StopIteration
'''
res = index()
print(next(res)) # 123
print(next(res)) # (123,111)
print(next(res)) # 报错
# 用for循环也可以 直接for自带报错处理
def index():
yield 123
yield 123,111
res = index()
for i in res:
print(i)
#123
#(123, 111)
自定义range方法
range方法其实是一个可迭代对象
'''
需求:通过生成器模拟range方法
for i in range(1,10):
print(i)
'''
# 先以两个参数的range参数为例
def my_range(start, end):
while start < end:
yield start
start += 1
for i in my_range(1,10):
print(i)
# 初步实现之后 在考虑不同参数情况 一个参数 三个参数
# 先针对一个
'''
end可以不传值 应该设置成默认参数 end=None
代码层面做判断 将形参数据做替换处理
end=start
start=0
'''
def my_range(start, end=None):
if not end:
end = start
start = 0
while start < end:
yield start
start += 1
for i in my_range(10):
print(i)
# 针对三个参数情况
'''
给函数添加第三个形参 并且设置成默认参数 默认值是1 step=1
每次递增只需要递增step的数值即可
'''
def my_range(start, end=None, step=1):
if not end:
end = start
start = 0
while start < end:
yield start
start += step
for i in my_range(1, 10, 2):
print(i)# 1,3,5,7,9
for i in my_range(10):
print(i)# 0,1,2,3,4,5,6,7,8,9
for i in my_range(1,10):
print(i) # 1,2,3,4,5,6,7,8,9
yield关键字作用
1.在函数体代码中出现 可以将函数变成生成器
2.在执行过程中,可以将后面的值返回出去,类似于return
3.还可以暂停住代码的运行
4.还可以接收外界的传值
yield接收外界传值 用语法.send()
def eat(name):
print(f'{name} 准备干饭')
while True:
food = yield
print(f'{name} 正在吃{food}')
res = eat('大胃王')
# 想执行一次,如果想执行多次直至结束 可以直接用for循环
res.__next__()
# yield 可以接收外部传值
res.send('鱼香肉丝') # 可以给yield传值,并且自动调用一次__next__方法
res.send('宫保鸡丁')
# 大胃王 准备干饭
# 大胃王 正在吃鱼香肉丝
# 大胃王 正在吃宫保鸡丁
生成器表达式
也是为了节省存储空间
重点:
生成器内部的代码只有在调用双下next迭代取值的时候才会执行
面试题示例
# 普通求和函数
def add(n,i):
return n+i
# 生成器对象 返回 1,2,3
def test():
for i in range(4):
yield i
# 将test函数变成生成器对象
g=test()
# 简单for循环
for n in [1,10]:
g = (add(n,i) for i in g)
'''
从上到下还没有调用函数 只有运行两次for循环,只有对g进行迭代取值是才进行代码操作
第一次for循环
g = (add(n,i) for i in g)
第二次for循环
g = (add(n,i) for i in (add(n,i) for i in g))
再list(g)调用函数时 第二次for循环里的n都为10,而i是0
所以第一次调用的时候就是10+10 20是起始数
'''
print(n)
res = list(g)# list底层就是for循环 相当于对g做了迭代取值操作
print(res)
# A .res=[10,11,12,13]
# B .res=[11,12,13,14]
# C .res=[20,21,22,23] # 正确答案
# D .res=[21,22,23,24]
# 正确答案是c 诀窍就是抓n是多少即可
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY