Python-迭代器、生成器、生成式
迭代器
概念
迭代取值的工具,迭代是一个重复的过程,每次重复都是基于上一次的结果而继续的,单纯的重复并不是迭代
为什么要有迭代器
迭代器是用来迭代取值的工具,而涉及到把多个值循环取出的类型有:列表、字符串、元组、字典、集合、打开的文件,而可以通过索引取值的只有列表、元组、字符串),为了解决基于索引取值的局限性,必须有一种不依赖索引的取值方式,即迭代器
可迭代对象有哪些
但凡内置有__iter__方法的对象,在python中都成为可迭代对象,所以,python中所有的数据类型都是可迭代对象,python中所有数据类型都有__iter__(),所以python中所有的数据类型都是可迭代对象
lst = [1, 2] s = 'hello world' dic = {'name': 'hushow', 'age': 18} tup = ('s', 1) sett = {1, 2, 's'} with open(r'tests.py', 'r', encoding='utf-8') as f: # 打开的文件即使可迭代对象也是迭代器对象 print(type(f), f.__iter__()) print(type(lst), lst.__iter__()) print(type(s), s.__iter__()) print(type(dic), dic.__iter__()) print(type(tup), tup.__iter__()) print(type(sett), sett.__iter__()) # <class '_io.TextIOWrapper'> <_io.TextIOWrapper name='tests.py' mode='r' encoding='utf-8'> # <class 'list'> <list_iterator object at 0x0000022825EDFF40> # <class 'str'> <str_iterator object at 0x0000022825EDFF40> # <class 'dict'> <dict_keyiterator object at 0x0000022827FEDDB0> # <class 'tuple'> <tuple_iterator object at 0x0000022825EDFF40> # <class 'set'> <set_iterator object at 0x0000022828BD7C40>
使用
调用可迭代对象的__iter__(),迭代取值
dic = {'a': 1, 'b': 2, 'c': 3} # 字典是可迭代对象,如果不调用其__iter__(),就没有__next__()方法,去迭代取值的
print(dic.__next__) # 报错
# 调用__iter__(),转为迭代器对象
dic_iter = dic.__iter__()
print(dic.__next__())
print(dic.__next__()) # 可以多次调用,如果没值的话,就会抛异常了
通过while迭代取值
dic = {'a': 1, 'b': 2, 'c': 3}
d_iterator = dic.__iter__() # 将字典转换为可迭代迭代器
while True:
try:
print(d_iterator.__next__())
except StopIteration: # 将值取完后,会抛异常
break
print('-->')
d_iterator = dic.__iter__() # 再次将字典转换为可迭代迭代器,才有值可取
while True:
try:
print(d_iterator.__next__())
except StopIteration:
break
可迭代对象与迭代器对象区别
可迭代对象
内置有__iter__()的对象,可以通过调用__iter__(),转为迭代器对象
迭代器对象
内置有__next__()的对象,并且也内置了__iter__()
可迭代对象调用了__iter__(),得到了迭代器对象,迭代器对象调用了__iter__(),还是得到的迭代器对象,还是它自己
dic = {'a':1, 'b':13}
d_iterator = dic.__iter__() # 转换为迭代器对象
print(d_iterator is d_iterator.__iter__() is d_iterator.__iter__().__iter__()) # True
为什么迭代器对象多次调用__iter__(),还是原始值?
因为for循环后面跟的不管是迭代器对象还是可迭代得到,都会调用其__iter__(),为了让for循环统一起来 ,所以迭代器对象也有这个方法
迭代器优缺点
优点
1、为可以索引取值的和不能索引取值的数据类型提供了统一的迭代取值的方式
2、惰性取值,迭代器对象表示的是一个数据流,可在需要时才调用__next__()来计算出一个值。就迭代器本身而言,同一时刻在内存中只有一个值,因而可以存放无限大的数据流。而对其他“容器”类型,如列表而言,需要把所有元素都放在内存中,所以受内存限制,那么存放的值的个数就是有限的
缺点
1、除非取完,否则无法获取迭代器的长度
2、只能取下一个值,无法回到开始,像是“一次性的”,迭代器产生后的唯一目的就是重复执行__next__(),直到值取完,否则就会停留在某个地方,等待下一次调用__next__()。若是要再次迭代同个对象,只能重新调用__iter__(),去重新创建一个新的迭代器对象,如果有两个或多个循环使用同一个迭代器,必然只有一个循环能取到值
for循环工作原理
1、可迭代对象.__iter__(),得到一个迭代器对象
2、迭代器对象.__next__(),拿到返回值,赋值给for后面的变量
3、重复步骤2,知道抛出异常StopIteration,for循环捕捉异常,然后结束循环
通过while实现
dic = {'name': 'hushow', 'age': 19}
dic_iter = dic.__iter__()
while True:
try:
print(dic_iter.__next__())
except StopIteration:
break
for循环写法
dic = {'name': 'hushow', 'age': 19}
for k in dic: # 等同于dic.__iter__()
print(k)
生成器
1、自定义的迭代器就是生成器
2、生成器就是迭代器
自定义迭代器
在函数内,一旦存在yield关键字,那么,调用这个函数,并不会执行函数体代码,而是会返回一个生成器对象,即自定义迭代器
def func():
print('第一次')
yield 1
print('第二次')
yield 2
g = func()
print(type(g)) # <class 'generator'>
print(g.__next__()) # 打印 第一次 1
print(g.__next__()) # 打印 第二次 2
print(g.__next__()) # 调用完了,抛StopIteration异常
自定义生成器
自定义range
def my_range(start, stop, step=1): while start < stop: yield start start += step g = my_range(1, 9, 2)
print(g.__next__()) print(next(g)) # next() 和 xx.__next__()是一样的,除了__next__(),还有iter()和__iter__()、len()和__len__()
for i in my_range(1,9,2): print(i)
def add(): count = 0 while True: count += 1 yield count g = add() print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__())
# 1 2 3 4 5
yield用法解释
x = yield 123
若使用res = g.send('apple'),那么,将apple传给了yield,然后赋值给了x,并返回了123给res
同时,程序一旦碰到了yield,就会挂起,然后返回数据,知道下次再调用,默认会返回None
通过一个例子来看执行流向
def eat(name): print('%s准备吃东西了...' %name) food_list = [] while True: x = yield food_list print('%s吃了%s' %(name,x)) food_list.append(x) g = eat('hushow') # 不执行函数体代码
print(type(g)) # <class 'generator'> 得到一个生成器对象 res = g.send(None) # 创建生成器,执行函数体代码 print(res) res = g.send('apple') print(res)# alex准备吃东西了...# []# alex吃了apple# ['apple']
执行过程
1、首先执行 g = eat('hushow'),并不会执行函数体代码,不调用g,那么就不会执行函数体代码
2、然后 res = g.send(None),一个生成器,第一次传入值,必须是None,然后开始执行函数体代码(print('%s准备吃东西了...' %name))
3、执行food_list = [] 定义一个空列表
4、执行while循环,然后就到了 x = yield food_list,一旦碰到了yield,那么该函数就会挂起,执行后续代码。且,由于是res = g.send(None)进入到函数体的,所以x = yield food_list这个写法的意思是,将None给yield,然后赋值给x,然后返回food_list,即返回一个空列表给res
5、从第二步 res = g.send('None')进入函数,执行函数体代码,然后函数挂起后,执行后续的代码,即print(res),即将上一步得到的空列表打印
6、然后执行res = g.send('apple'),又给生成器对象传了一个值apple,即将apple传给了yield,然后赋值给了x
7、然后执行print('%s吃了%s' %(name,x)),打印hushow吃了apple
8、然后执行food_list.applend() 将apple追加到列表中
9、然后重复while循环,到了x = yield food_list
10、函数挂起,后续没代码了,程序结束
生成式
三元表达式
普通语法
def func(x, y): if x > y: return x else: return y res = func(1, 2) print(res)
三元表达式写法
def func(x, y): return x if x > y else y # 如果x大于y,返回x,否则返回y res = func(1, 2) print(res)
列表生成式
格式
[expression for item in iterable if condition]
例子
new_lst = []
lst = ['wx', 'alex', 'bob', 'lxx']
res1 = [item for item in lst if item.endswith('x')]
res2 = [item.upper() for item in lst]
res3 = [item.replace('x','_nb') for item in lst if item.endswith('x')]
print(res1)
print(res2)
print(res3)
字典生成式
item = {'hushow', 'tom', 'jerry'}
dic = {i: None for i in item}
print(dic)
lst = [('name', 'hushow'), ('age', 18), ('gender', 'male')] dic = {k: v for k, v in lst if k != 'gender'} print(dic)
比较两种写法
l = ['wx_nb', 'alex_nb', 'bob_nb', 'lxx'] dic1 = [{item: None for item in l}] # 生成一个列表,套一个大字典 dic2 = [{item: None} for item in l] # 生成一个列表,套一个个小字典 print(dic1) # [{'wx_nb': None, 'alex_nb': None, 'bob_nb': None, 'lxx': None}] print(dic2) # [{'wx_nb': None}, {'alex_nb': None}, {'bob_nb': None}, {'lxx': None}]
生成器生成式
没有元组生成式,这里的小括号不是元组
res = (i for i in range(10)) print(res) # <generator object <genexpr> at 0x0000021751047890> print(type(res)) # <class 'generator'> print(res.__next__()) print(res.__next__()) print(res.__next__())
计算文件的字符
with open(r'tests.py','rt',encoding='utf-8') as f: length = sum(line.__len__() for line in f) # 一行行读出来,然后长度累加 print(length)
END.
浙公网安备 33010602011771号