概述
1.迭代器协议:
对象必须提供一个__next__()方法,执行该方法要么返回迭代中的下一项,要么就捕捉一个StopIteration异常,已终止迭代(只能往后走不能往前退)2.可迭代对象:
实现迭代协议对象(如何实现:对象内部定义一个__iter__()方法)3.协议是一种约定,可迭代对象实现了迭代器协议,python内部工具(如for循环,sum、min、max函数等)使用迭代器协议访问对象
for循环机制
for循环的本质:循环所有对象,全都是使用迭代器协议
1.迭代器
对于序列类型:字符串、列表、元组,我们可以通过索引的方式迭代取出其包含的元素。但对于非序列类型:字典、集合、文件是没有索引的,若想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器2.可迭代对象
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
open('a').__iter__3.迭代器对象
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象文件类型是迭代器对象
open('a').__iter__()
open('a').__next__()注:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
迭代器对象
l = [1, 2, 3]
# 一:下标访问方式
print(l[0])
print(l[1])
print(l[2])
print(l[3]) # 列表超出索引边界:IndexError
'''
结果
1
2
3
IndexError: list index out of range
'''
# 使用迭代器协议访问,不需要依赖索引迭代取值了
iter_l = l.__iter__() # 遵循迭代器协议,生成可迭代对象就是迭代器对象
while True:
try:
k = next(iter_l) # 可迭代器对象调用__iter__中的内置__next__方法
print(k)
except StopIteration: # 抛出StopIteration异常,并终止迭代
break
'''
结果:
1
2
3
'''
# 二:遵循迭代器协议访问方式,迭代器允许惰性求值,只有在请求下一个元素时迭代器对象才会去生成它
x = 'Alex'
iter_x = x.__iter__() # 得到迭代器对象,可迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
print(iter_x.__iter__()is iter_x) # True
# 打印字符串迭代器对象地址
print(iter_x)
# <str_iterator object at 0x10a5c2e80>
print(iter_x.__next__()) # 迭代器对象调用__iter__中的内置__next__方法,等同于next(iter_x),next()是系统内置函数
print(iter_x.__next__()) # 每次打印一个字符串的字符
print(iter_x.__next__())
print(iter_x.__next__())
print(iter_x.__next__()) # 捕捉StopIteration异常,终止迭代
'''
结果:
A
l
e
x
StopIteration
'''
for循环
dit = {'name': '3+2', 'age': 18, 'sex': '无'}
for i in dit:
print(i)
'''
for循环工作原理:
1.执行in后面的对象dit.__iter__()方法,得到一个迭代器对象iter_dit
2.执行next(iter_dit),将得到的值赋值给i,然后执行循环体代码
3.依次循环,直到捕捉StopIteration异常,终止循环
'''
注:for循环是遵循迭代器协议去执行操作,与索引没有任何关系
为什么要用for循环
# 迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
l = [1, 2, 3]
num = 0
while num < len(l): # 迭代
print(l[num])
num += 1
序列类型:字符串、列表、元组都有下标,是可以通过下标取值,但是非序列类型:字典、集合、文件对象,是不能通过下标取值的,for循环是基于迭代器协议提供一个统一遍历所有对象的方法,但遍历对象之前,对象调用__iter__()方法将生成一个可迭代器对象,然后迭代器协议去实现循环访问,所有的对象都可以通过for循环来遍历
迭代器的优缺点
优点:
提供一种统一的、不依赖于索引的迭代方式
惰性计算,节省内存
缺点:
无法获取长度(只有在next完毕才知道到底有几个值)
一次性的,只能往后走,不能往前退
生成器
生成器
一种数据类型,自动实现迭代器协议,对象只需要调用内置__next__()方法(其他的数据类型需要调用自己内置的方法__iter__()方法,生成器就是可迭代对象
生成器分类即python中的表现形式:(Python有两种不同的方式提供生成器)
1.生成器函数:常规函数定义,但是,yield关键字代替了return语句返回结果,yield关键字一次返回一个结果,在每个结果中间,保留函数的状态,以便下次从它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
- 语法上和函数类似:生成器函数和常规函数几乎是一样的,他们都是使用def语句进行定义,差别在于,生成器使用yield关键字可以返回多个值,而常规函数使用return语句只能返回一个值
- 自动实现迭代器协议:对于生成 器,Python会自动实现迭代器协议,以便应用到迭代背景中(如for循环、sum函数)由于生成器自动实现了迭代器协议,所以,我们可以调用他的next()方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常
- 状态挂起:生成器使用yield关键字返回一个值,yield关键字保留生成器函数的状态,以便之后从它离开的地方继续执行
生成器函数
'''
只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码,需要生成器对象调用__next__()/next()方法,才可以执行函数内部代码
1、把函数做成迭代器
2、对比return,可以返回多次值,可以挂起/保存函数的运行状态
'''
def func():
stay = yield '优乐美'
print('停留的位置', stay)
yield '香飘飘'
# gen_res是一个生成器对象
gen_res = func()
# 打印生成器对象地址
print(gen_res)
# <generator object test at 0x10d7e8408>
print(gen_res.__next__()) # 生成器对象调用__next__()/next()方法,保留函数状态
print(next(gen_res)) # 从上一次保留函数状态地方,继续往下执行操作
print(gen_res.send('stay')) # 表达形式,yield 1i相当于return控制的是函数的返回值,stay = yield的另外一个特性,接受send传过来的值,赋值给stay,send传入的值和yield返回的值没有任何关系
'''
结果:
优乐美
香飘飘
'''
三元表达式
coffemilk = '优乐美'
coffemilk = 'Tea'
res = '喜之郎' if coffemilk == '优乐美' else '茶'
print(res)
列表解析(列表推导式)
list_res = ['蘑菇%s' % i for i in range(1, 11) if i <= 6] # 内存占用大,容易卡死
print(list_res)
'''
结果:
['蘑菇1', '蘑菇2', '蘑菇3', '蘑菇4', '蘑菇5', '蘑菇6']
'''
错误示例:
list_res = ['蘑菇%s' % i for i in range(1, 11) else '采蘑菇的小姑娘'] # 在列表解析中不能使用else语句,会标红
生成器表达式
# 生成器表达式自动实现迭代器协议,迭代器协议对象调用__next__()/next()方法
murphy = ('土豆%s' % i for i in range(1, 11)) # 几乎不占内存
# 打印生成器对象地址
print(murphy)
# <generator object <genexpr> at 0x104502408>
print(murphy.__next__()) # 生成器对象调用__next__()方法
print(next(murphy)) # next()内置函数,next()本质调用__next__(),
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
# print(next(murphy)) # 捕捉StopIteration异常,终止迭代
'''
结果:
土豆1
土豆2
土豆3
土豆4
土豆5
土豆6
土豆7
土豆8
土豆9
土豆10
StopIteration
'''
注:生成器只能遍历一次
1.把列表解析[]换成()得到的生成器表达式
2.列表解析与生成器表达式都是一种遍历的编程方式,生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如,sum函数....是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议
print(sum(i for i in range(10)))
总结
- 生成器的好处是延迟计算,一次返回一个结果,它不会一次生成所有的结果,这对于大数据量处理,会非常有用
- 生成器还能有效提高代码可读性
- 生成器允许你以一种非常pythonic的方式来创建迭代器。
最后
以上就是繁荣康乃馨为你收集整理的迭代器、for循环、生成器的全部内容,希望文章能够帮你解决迭代器、for循环、生成器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复