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__()注:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
迭代器对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49l = [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循环
1
2
3
4
5
6
7
8
9dit = {'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循环
1
2
3
4
5
6# 迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 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关键字保留生成器函数的状态,以便之后从它离开的地方继续执行
生成器函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24''' 只要函数内部包含有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返回的值没有任何关系 ''' 结果: 优乐美 香飘飘 '''
三元表达式
1
2
3
4
5coffemilk = '优乐美' coffemilk = 'Tea' res = '喜之郎' if coffemilk == '优乐美' else '茶' print(res)
列表解析(列表推导式)
1
2
3
4
5
6
7
8list_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语句,会标红
生成器表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30# 生成器表达式自动实现迭代器协议,迭代器协议对象调用__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的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议
1print(sum(i for i in range(10)))
总结
- 生成器的好处是延迟计算,一次返回一个结果,它不会一次生成所有的结果,这对于大数据量处理,会非常有用
- 生成器还能有效提高代码可读性
- 生成器允许你以一种非常pythonic的方式来创建迭代器。
最后
以上就是繁荣康乃馨最近收集整理的关于迭代器、for循环、生成器的全部内容,更多相关迭代器、for循环、生成器内容请搜索靠谱客的其他文章。
发表评论 取消回复