温故而知新, 重新看一下以前做的笔记。
生成器
什么是生成器?
生成器是一个特殊的迭代器(迭代器的抽象级更高)。
兼具迭代器的特性
- 惰性计算数据, 节省内存
什么是惰性计算数据, 就是把数据 分成N份, 每次处理一份, 处理完成后丢弃掉, 每次都不需要考虑什么, 调用就可以获取一份数据, 用完就丢弃 - 能够记录状态, 并通过 next() 函数,访问下一个状态
- 具有可迭代特性
但是如果打造一个自己的迭代器, 比较复杂, 所以就有了一个更加优雅的方式 ‘生成器’ , 所以说 生成器是一种迭代器, 但是 迭代器不一定是生成器
什么是抽象级更高? 比如人是动物, 但是动物不一定是人, 因此动物的抽象级更高, 而人就更加具象化生成器的创建方式
生成器表示式
把列表推导式的 [] 修改成 () 即可
运行效果如下# 列表推导式 member = [i for i in range(1, 10) if i % 2 != 0] # 生成器,把列表推导式的 [] 改成 () 即可 sc = (i for i in range(1, 10) if i % 2 != 0) print(member) print("-----------") print(sc)
我们用列表推导式生成的那么长的数据, 有时候不一定有的完,所以可能会造成内存浪费, 因此我们就可以使用生成器, generator 是生成器的意思[1, 3, 5, 7, 9] ----------- <generator object <genexpr> at 0x0000022412A72B30>
生成器函数
生成器函数: 函数中包含 yield语句, 这个函数的执行结果就是”生成器”, yield value 是状态值, 当代码执行到yield 语句的时候, 会停下来记录状态值, 并且把 value 返回出去def test(): print("come on") yield 1 print("again") yield 2 print("last") yield 3 print("再来一次,后面没有yield") # yield 10086 g = test() # <generator object test at 地址值> print(g) print(next(g)) print("------") print(next(g)) print("------") print(next(g)) # 再来一次,就报错了, 补上 yield 10086 就不会了 print("------") print(next(g)) # # 即使补上 yield 10086, 这里也会报错,因为后面没有yield 返回值了,就不能继续使用next() 了 # print("=====") # print(next(g))
生成器举例
也可以用在生成器函数中, 使用到for 循环来生成 field状态值
def test():
print("come on")
for i in range(10):
print("doing")
yield i
print("action")
g = test()
print(next(g))
print("------")
print(next(g))
print("------")
print(next(g))
运行效果如下
come on
doing
0
------
action
doing
1
------
action
doing
2
生成器产生数据的方式
生成器具有可迭代性, next() 函数, 等价于 生成器.__next__() 也可以用 for in 来遍历
生成器函数里的yield语句特点:遇到 yield就会停下来
def test():
print("come on")
for i in range(10):
print("doing")
result = yield i
print("action")
print("看看赋值结果 " + str(result))
g = test()
print(next(g))
print("------")
print(next(g))
print("------")
print(next(g))
运行效果如下
come on
doing
0
------
action
看看赋值结果 None
doing
1
------
action
看看赋值结果 None
doing
2
进程已结束,退出代码 0
这里也看出, 通过普通的赋值语句, 拿不到 yield 语句的 yield 状态值, yeid 的 返回值并不重要, 它只是起到一个阻断代码的作用
因此搭配 for in 来使用效果更好
def test():
print("come on")
for i in range(5):
print("****第%s 次进来这里****" % (i+1))
yield i
print("action")
print("第%s 次阻断代码" % (i+1))
g = test()
for i in range(5):
# 等价于next(g)
g.__next__()
# 这一行会报错StopIteration。 迭代器要复用,必须要回退,生成器也是属于迭代器
# g.__next__()
运行效果
come on
****第1 次进来这里****
action
第1 次阻断代码
****第2 次进来这里****
action
第2 次阻断代码
****第3 次进来这里****
action
第3 次阻断代码
****第4 次进来这里****
action
第4 次阻断代码
****第5 次进来这里****
生成器的特点之必须实例化才能使用
生成器必须要实例化才可以使用, 否则记录不了状态, 在第一个 yeild 语句就会停下来
def test():
print("come on")
for i in range(5):
print("****第%s 次进来这里****" % (i+1))
yield i
print("action")
print("第%s 次阻断代码" % (i+1))
test().__next__()
test().__next__()
test().__next__()
运行效果
come on
****第1 次进来这里****
come on
****第1 次进来这里****
come on
****第1 次进来这里****
生成器的send 方法
send 方法有一个参数,指定的是上一次被挂起的yield 语句的返回值;
相比于.__next__() , 可以额外的给yield 语句传值
注意第一次调用 t.send(None)
以下是代码示范
def test():
print("开始")
# 遇到yield 语句就会停下来,所以赋值语句是没有执行到的
result = yield 1
print("看看赋值结果, %s" % result)
result = yield 2
print("看看赋值结果, %s" % result)
result = yield 3
print("看看赋值结果, %s" % result)
result = yield 4
print("看看赋值结果, %s" % result)
g = test()
g.__next__()
g.__next__()
# send 是给上一个yield 的返回值进行赋值, 此时结合代码来看是给yield 2 那里给赋值了
g.send("今宵酒醒何处")
g.__next__()
运行效果
开始
看看赋值结果, None
看看赋值结果, 今宵酒醒何处
看看赋值结果, None
send方法的注意事项
在没有启动过生成器的情况下, 首次启动生成器就使用 send() 方法, 发送None 是可以的, 发送None 在此时可以认为是启动了生成器,但是这个None并不会给yield 赋值.
但是此时发送其他值,则会报错的。 这里就不做代码演示了。
关闭生成器 g.close()
def test():
print("开始")
# 遇到yield 语句就会停下来,所以赋值语句是没有执行到的
result = yield 1
print("看看赋值结果, %s" % result)
result = yield 2
print("看看赋值结果, %s" % result)
result = yield 3
print("看看赋值结果, %s" % result)
result = yield 4
print("看看赋值结果, %s" % result)
g = test()
g.__next__()
g.__next__()
# send 是给上一个yield 的返回值进行赋值, 此时结合代码来看是给yield 2 那里给赋值了
g.send("今宵酒醒何处")
g.__next__()
# 关闭生成器后,还继续调用,也会报错StopIteration, 但是会把生成器的位置放在最后一个yield 了
g.close()
g.__next__()
运行效果
E:\pythonScript\Blog\venv\Scripts\python.exe E:/pythonScript/Blog/plance.py
Traceback (most recent call last):
File "E:/pythonScript/Blog/plance.py", line 20, in <module>
g.__next__()
StopIteration
开始
看看赋值结果, None
看看赋值结果, 今宵酒醒何处
看看赋值结果, None
进程已结束,退出代码 1
生成器的注意事项(1)遇到return 会停止并且抛出异常
遇到 return 就会停止, 并且也会抛出一个异常, 返回值都一起显示
def test():
print("开始")
# 遇到yield 语句就会停下来,所以赋值语句是没有执行到的
result = yield 1
print("看看赋值结果, %s" % result)
# return 后面的就不会执行了
return 100
result = yield 2
print("看看赋值结果, %s" % result)
result = yield 3
print("看看赋值结果, %s" % result)
result = yield 4
print("看看赋值结果, %s" % result)
g = test()
g.__next__()
next(g)
运行效果
Traceback (most recent call last):
File "E:/pythonScript/Blog/plance.py", line 16, in <module>
next(g)
StopIteration: 100
开始
看看赋值结果, None
(2)也可以用 for in 迭代器运行, 并且还可以迭代一次
def test():
print("开始")
# 遇到yield 语句就会停下来,所以赋值语句是没有执行到的
result = yield 1
print("看看赋值结果, %s" % result)
# return 后面的就不会执行了
result = yield 2
print("看看赋值结果, %s" % result)
result = yield 3
print("看看赋值结果, %s" % result)
result = yield 4
print("看看赋值结果, %s" % result)
g = test()
for i in g:
print(i)
运行效果
开始
1
看看赋值结果, None
2
看看赋值结果, None
3
看看赋值结果, None
4
看看赋值结果, None
用 for in 可以把最后一个 yield 语句后面的 语句给执行了, 不会像 g.next() 那样抛出异常. 自己根据情况选择。
因为生成器本身也是迭代器, 因此它只能够迭代使用一次。
但是如果想要这个生成器里的东西重复运行呢?
那就重新实例化一个生成器对象就可以了, 这样就可以继续运行了