前言
之前讲到set的时候,说到set没有查找其中元素的函数,我们可以通过循环迭代出set中的所有key。
介绍
迭代
首先先介绍迭代,其实就是通过for循环遍历序列,可以访问到序列中所有的元素,如:
for i in list(range(0, 3)):
print(i, end=' ') # 0 1 2
- 只要是可迭代对象都是可以迭代,像set和dict我们都不知道key的位置,但是它们是可迭代对象所以自然可以迭代。通过isinstance判读是否为可迭代对象,记得导入模块。
- 能够通过for循环直接作用的这些对象都是可迭代对象 (Iterable)。
from collections.abc import Iterable
l = {1: "aa", 2: "bb",3:"cc"}
print(isinstance(l, Iterable))#True
没有key的位置,一样可以迭代可迭代对象,如字典:
l = {1: "aa", 2: "bb", 3: "cc"}
for k, v in l.items():
print(k, v, end=";") # 1 aa;2 bb;3 cc;
迭代器
- 首先,可以被next函数调用的对象就是迭代器(Iterator)。
- 我们看前面的示例都是通过for循环遍历到元素然后输出,而迭代器是一个可以记住遍历位置的对象。我们通过迭代器迭代到的元素,控制迭代(迭代器的迭代只能迭代下去,不能回退的)。
- 是可迭代对象(Iterable)不代表可以被next函数调用,需要将其转为迭代器(Iterator)才能被next。
l = {1: "aa", 2: "bb", 3: "cc"}
it = iter(l) # 创建迭代器
key = next(it) #迭代
print("key:", key, "value:", l.get(key)) # key: 1 value: aa
key = next(it)
print("key:", key, "value:", l.get(key)) # key: 2 value: bb
这样一次一次next太麻烦了,我们可以配合循环使用迭代器。StopIteration异常标志着循环结束,防止无限循环的发生。
import sys
l = {1: "aa", 2: "bb", 3: "cc"}
it = iter(l) # 创建迭代器
while True: # 循环开始
try:
key = next(it)
print("key:", key, "value:", l.get(key)) # key: 1 value: aa
except StopIteration:
# break
sys.exit() # 没有下一个key退出循环
生成器
- 一边循环一边计算的机制是生成器(generator)。
- 含有yield的函数皆为生成器。
- 生成器不仅可以直接作用于for循环也可以被next函数调用,算是将Iterable和Iterator结合了。所以,生成器也算是一种迭代器了。
创建生成器:
1.将一个列表生成式变为生成器
print(i * i for i in [1, 2, 3, 4])
# <generator object <genexpr> at 0x000001FF47D84CF0>
2.或构造带yield的函数
yield可理解为return,return也意味着循环的结束。
def change(num):
for x in range(num):
result = yield x # yield =>return x
print("result: ", result)
g = change(3)
print(next(g))
print('-----------')
print(next(g))
print('-----------')
print(next(g))
'''
0
-----------
result: None
1
-----------
result: None
2
'''
分析代码与结果:
- 函数change因为有了yield所以变成了生成器,我们这里使用next函数驱使其迭代下去。
- 第一次next进入函数中的循环,x为0,yield x=》return
0,此时函数结束,同时result没有赋值。而yield与一般return不同,当下一次next生成器时,会跳转到上次停止的地方,也就是result赋值操作。 - 第二次next,接入上次操作,result赋值,可惜 x已经被上一次的操作return了,所以不存在为None赋值给result。但是此时不像上一次并没有return退出,所以循环继续,yield x,return 1。
之前写的代码还可以更改,通过for循环隐式next迭代,结果与图上是一样的。
def change(num):
for x in range(num):
result = yield x # yield =>return x
print("result: ", result)
g = change(3)
for i in g:
print(i)
对于生成器我们不仅可以使用next迭代,还可以调用send函数,功能如何请看示例:
def change(num):
for x in range(num):
result = yield x # yield =>return x
print("result: ", result)
g = change(3)
print(next(g))
print('-----------')
print(g.send(3))
print('-----------')
print(next(g))
'''
0
-----------
result: 3
1
-----------
result: None
2
'''
分析结果:
- 第一次的next老样子,到了yield x 跳出了函数,最后结束位置在result赋值。
- 此时生成器使用了send函数传值3,继续迭代。接入上次结束位置,result=3,继续循环x变为1,返回1跳出函数。后面的结果一样就不分析了。
注意:send函数如果不传参,效果和next函数一样。
总结
我这次文章在生成器上花的时间更多一些,一开始对于yield特别的不理解,幸亏csdn上人才辈出,看了几篇博客后终于弄清楚了。也希望我这篇文章能或多或少帮助大家理解吧。