迭代器与生成器
Posted williamkong94
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迭代器与生成器相关的知识,希望对你有一定的参考价值。
目录
迭代器
迭代器:迭代的工具。迭代可以看成重复,并且每一次的重复都是基于上次的结果来的,不是单纯的重复。
可迭代对象
python中一切皆对象,如:
x = 1
name = 'william'
lis = [1, 2, 3, 4]
tup = (1, 2, 3, 4)
dic = 'name': 'william'
s = 'a', 'b'
def func():
pass
f = open('602.txt', 'w', encoding='utf8')
对于这一切对象,只要有__iter__
方法的对象,都是可迭代对象。
# x = 1.__iter__ # SyntaxError: invalid syntax
# 以下都是可迭代对象
name = 'william'.__iter__
lis = [1, 2, 3, 4].__iter__
tup = (1, 2, 3, 4).__iter__
dic = 'name': 'william'.__iter__
s = 'a', 'b'.__iter__
f = open('602.txt', 'w', encoding='utf8')
f.__iter__
f.close()
总结
可迭代的对象:Python内置的str,list,tuple,dict,set,file都是可迭代对象。
特点:内置有__iter__
方法的都叫可迭代的对象。
迭代器对象
只有字符串和列表依赖索引取值,其他的可迭代对象都是无法依赖索引取值的。因此我们得找到一个方法能让其他的可迭代对象不依赖索引取值。
首先我们要知道迭代器对象的概念:可迭代对象执行__iter__
方法得到的返回值。并且迭代器对象会有一个__next__
方法。
# 不依赖索引的数据类型迭代取值
dic = 'a': 1, 'b': 2, 'c': 3
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
# print(iter_dic.__next__()) # StopIteration:
a
b
c
# 依赖索引的数据类型迭代取值
lis = [1, 2, 3]
iter_lis = lis.__iter__()
print(iter_lis.__next__())
print(iter_lis.__next__())
print(iter_lis.__next__())
# print(iter_lis.__next__()) # StopIteration:
1
2
3
上述的方法是非常繁琐的,我们可以使用while循环精简下。其中使用的try except
为异常处理模块。
s = 'hello'
iter_s = s.__iter__()
while True:
try:
print(iter_s.__next__())
except StopIteration:
break
h
e
l
l
o
总结
迭代器对象:执行可迭代对象的__iter__
方法,拿到的返回值就是迭代器对象。
特点:
- 内置有
__next__
方法,执行该方法会拿到迭代器对象中的一个值。 - 内置有
__iter__
方法,执行该方法会拿到迭代器本身。 - 文件本身就是迭代器对象
缺点:
- 取值麻烦,只能一个一个取,并且只能往后取,值取了就没了。
- 无法使用len()方法获取长度。
for循环原理
for循环称为迭代器循环,in后必须是可迭代的对象。
lis = [1, 2, 3]
for i in lis:
print(i)
1
2
3
因为迭代器使用__iter__
后还是迭代器本身,因此for循环不用考虑in后的对象是可迭代对象还是迭代器对象。
由于对可迭代对象使用__iter__
方法后变成一个迭代器对象,这个迭代器对象只是占用一小块内存空间,只有使用__next__
后才会吐出一个一个值。如lis = [1,2,3,4,5]
相当于一个一个鸡蛋,而lis = [1,2,3,4,5].__iter__
相当于一只老母鸡,如果你需要蛋,只需要__next__
即可。
# python2中
print(range(10)) # [0,1,2,3,4,5,6,7,8,9]
# python中
print(range(10)) # range(0,10)
三元表达式与列表推导式
三元表达式
‘条件成立时的返回值‘if
‘条件‘else
‘条件不成立时的返回值‘。
x = 10
y = 20
print(f'x if x > y else y : x if x > y else y')
x if x > y else y : 20
列表推导式
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
类似于
res = []
for item1 in iterable1:
if condition1:
for item2 in iterable2:
if condition2:
...
for itemN in iterableN:
if conditionN:
res.append(expression)
print(f'[i for i in range(8)] :[i for i in range(8)] ')
[i for i in range(8)] :[0, 1, 2, 3, 4, 5, 6, 7]
print(f'[i**2 for i in range(8)] : [i**2 for i in range(8)]')
[i**2 for i in range(8)] : [0, 1, 4, 9, 16, 25, 36, 49]
name_list = ['william', 'tom', 'jerry']
print(
f"[name if name == 'william' else name +'funny' for name in name_list]:[name if name == 'william' else name + 'funny' for name in name_list] ")
[name if name == 'william' else name +'funny' for name in name_list]:['william', 'tomfunny', 'jerryfunny']
字典生成式
字典生成式
print(i: i**2 for i in range(8))
0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49
zip()方法
keys = ['name', 'age', 'gender']
values = ['william', 18, 'male']
res = zip(keys, values)
print(f'zip(keys,values):zip(keys,values)')
info_dict = k: v for k, v in res
print(f'info_dict :info_dict')
zip(keys,values):<zip object at 0x00000256E9B28548>
info_dict :'name': 'william', 'age': 18, 'gender': 'male'
通过解压缩函数生成一个字典。
info_dict = 'name': 'william', 'age': 18, 'gender': 'male'
print(f'info_dict.keys():info_dict.keys()')
print(f'info_dict.values():info_dict.values()')
res = zip(info_dict.keys(), info_dict.values())
print(f'zip(keys,values):zip(info_dict.keys(),info_dict.values())')
info_dict = k: v for k, v in res
print(f'info_dict:info_dict')
info_dict.keys():dict_keys(['name', 'age', 'gender'])
info_dict.values():dict_values(['william', 18, 'male'])
zip(keys,values):<zip object at 0x00000256E9B28988>
info_dict:'name': 'william', 'age': 18, 'gender': 'male'
生成器
yield关键字
yield意思为生产,在函数中但凡出现yield关键字,再调用函数,就不会继续执行函数体代码,而是会返回一个值。
def func():
print(1)
yield
print(2)
yield
g = func()
print(g)
<generator object func at 0x00000256E9B0CFC0>
生成器的本质就是迭代器,同时也并不仅仅是迭代器,不过迭代器之外的用途实在是不多,所以我们可以说:生成器提供了非常方便的自定义迭代器的途径。
def func():
print('from func1')
yield 'a'
print('from func2')
yield 'b'
g = func()
print(f'g.__iter__ == g :g.__iter__() == g')
res1 = g.__next__()
print(f'res1:res1')
res2 = next(g)
print(f'res2:res2')
# next(g) # StopIteration
g.__iter__ == g :True
from func1
res1:a
from func2
res2:b
def func():
print('from func1')
yield 'a'
print('from func2')
yield 'b'
g = func()
for i in g:
print(i)
print(f'list(func():list(func()))')
from func1
a
from func2
b
from func1
from func2
list(func():['a', 'b'])
yield + return??
既然生成器也是函数,那么它可以使用return输出返回值么?
既然都选择自定义一个函数作为生成器,还return干什么,Python2中会报异常,Python中,无视这种行为。
def i_wanna_return():
yield 'a'
yield 'b'
return None
yield 'c'
for i in i_wanna_return():
print(i)
a
b
迭代器套迭代器
如果我需要在生成器的迭代过程中接入另一个生成器的迭代怎么办?写成下面这样好傻好天真。
def sub_generation():
yield 1
yield 2
for i in range(3):
yield i
for i in sub_generation():
print(i)
1
2
0
1
2
def sub_generation():
yield 1
yield 2
yield from range(3)
for i in sub_generation():
print(i)
1
2
0
1
2
协同程序
协同程序(协程)一般来说是指这样的函数:
- 彼此间有不同的局部变量、指令指针,但仍共享全局变量;
- 可以方便的挂起、恢复,并且有多个入口点和出口点;
- 多个协同程序间表现为协作运行,如A的运行过程中需要B的结果才能继续执行。
协程的特点决定了同一时刻只能有一个协同程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁、或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。
从另一方面说,协程无法并发其实也将他的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多被拿来与常规函数进行比较,而不是与线程。当然,线程比协程复杂许多,功能也更强大,所以牢牢掌握线程即可。
自定义range()方法
def my_range(start, stop, step=1):
while start < stop:
yield start
start += 1
g = my_range(0, 3)
print(f'list(g):list(g)')
list(g):[0, 1, 2]
总结
yield:
- 提供一种自定义迭代器的方式。
- yield可以暂停住函数,并提供当前返回值。
yield和return:
- 相同点:两者都是在函数内部使用,都可以返回值,并且返回值没有类型和个数的限制
- 不同点:return只能返回一次值,yield可以返回多次值。
生成器表达式
- 把列表推导式的[]换成()就是生成器表达式
- 优点:省内存,一次只产生一个值在内存中
t = (i for i in range(8))
print(t)
print(f'next(t):next(t)')
<generator object <genexpr> at 0x00000256EABFE150>
next(t):0
生成器表达式和列表推导式
列表推导式相当于直接给你一筐蛋,而生成器表达式相当于给你一只老母鸡。
# 生成器表达式
with open('602.txt', 'r', encoding='utf8') as f:
nums = [len(line) for line in f]
print(max(nums)) # len(william)
7
# 列表推导式
with open('602.txt', 'r', encoding='utf8') as f:
nums = (len(line) for line in f)
print(max(nums)) # ValueError: I/O operation on closed file.
以上是关于迭代器与生成器的主要内容,如果未能解决你的问题,请参考以下文章