生成器

1. 什么是生成器
生成器是一個返回迭代器的函數,只能用於迭代操作,更簡單點理解生成器就是一個迭代器。
不同於一般的函數會一次性返回包括了所有數值的數組,生成器一次只能產生一個值,這樣消耗的內存數量將大大減小,而且允許調用函數可以很快的處理前幾個返回值。
2 創建生成器
生成器可以通過生成器表達式和生成器函數獲取到
生成器表達式
創建生成器對象和創建列表生成式特別像
# 創建列表生成式
>>> list_num = [x for x in range(10)]
>>> list_num
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 生成器
>>> glist_num = (x for x in range(10)) # 只需要把中括號換成小括號就成了生成器
>>> glist_num
<generator object <genexpr> at 0x7fc3d7375830>
>>> glist_num.__next__()
0
>>> glist_num.__next__()
1
>>> next(glist_num)
2
>>> next(glist_num)
3
# 生成器表達式內部的代碼只有在迭代取值的時候才會執行
使用next()
和使用__next__()
一樣
生成器函數
生成器函數指的是函數體中包含yield
關鍵字的函數
定義生成器函數和定義普通函數一樣。
# 1 生成器函數
>>> def my_func():
print("Hello")
yield
>>> my_func()
<generator object my_func at 0x7fc3d6c70e08>
>>> res = my_func()
>>> res
<generator object my_func at 0x7fc3d6c70e60>
>>> next(res)
Hello
>>>
>>> next(res)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
# 生成器函數(yield有返回值)
def my_func():
print("Hello")
yield 1
res = my_func()
r = res.__next__() # 會執行 print("Hello")
print(r) # r為yield的返回值
# 執行結果:
Hello
1
# 生成器函數(yield多個返回值)
def my_func():
print("Hello")
yield 1,2
res = my_func()
r = res.__next__()
print(r)
# 執行結果:
Hello
(1, 2)
#函數中只有一個yield所以只能執行一次__next__()方法
def my_func():
print("Hello")
yield 1,2
res = my_func()
print(res.__next__())
print(res.__next__()) # 調用兩次就會報錯
#執行結果:
Hello
(1, 2)
Traceback (most recent call last):
File "b.py", line 8, in <module>
print(res.__next__())
StopIteration
# 有多個yield
def my_func():
print("Hello")
yield 1,2
print("world")
yield 'a','b'
res = my_func()
print(res.__next__()) # 每執行一個__next__代碼往下運行到yield停止 返回后面的數據
print(res.__next__()) # 每執行一個__next__代碼往下運行到yield停止 返回后面的數據
注意:當函數體內含有yield
關鍵字 那么在第一次調用函數的時候,並不會執行函數體代碼,而是將函數變成了生成器(迭代器).
每執行一個__next__
代碼往下運行到yield停止 返回后面的數據.
yield
不但能返回值,而且還能給它傳值
yield
傳值
使用send
給yield
傳值
示例:
def my_define(name):
print("%s is doing work" % name)
while True:
play = yield
print("%s is do %s" %(name, play))
res = my_define("Hans")
res.__next__()
res.__next__()
# 執行結果:
Hans is doing work
Hans is doing None
# 給yield傳值:
def my_define(name):
print("%s is doing work" % name)
while True:
play = yield
print("%s is do %s" %(name, play))
res = my_define("Hans")
res.__next__()
res.__next__()
# 給yield傳兩個值:write和coding
res.send("write")
res.send("coding")
# 執行結果:
Hans is doing work
Hans is do None
Hans is do write
Hans is do coding
3 生成器練習
模擬range功能
# 代碼
def my_range(start, stop=None, step = 1):
if not stop:
stop = start
start = 0
while start < stop:
yield start
start += step
print("一個參數")
for i in my_range(3):
print(i)
print("兩個參數")
for i in my_range(1,3):
print(i)
print("三個參數")
for i in my_range(1, 10, 2):
print(i)
# 執行結果:
一個參數
0
1
2
兩個參數
1
2
三個參數
1
3
5
7
9
求和(面試題)
# 代碼:
def add(n, i):
return n + i
def test():
for i in range(4):
yield i
g = test()
for n in [1, 10]:
g = (add(n, i) for i in g)
res = list(g)
print(res)
#從下面的結果中選擇一個:
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]
#D. res=[21,22,23,24]
# 解析:
def add(n, i):
return n + i
# 調用之前是函數 調用之后是生成器
def test():
for i in range(4):
yield i
g = test() # 初始化生成器對象
for n in [1, 10]:
g = (add(n, i) for i in g)
"""
第一次for循環
g = (add(n, i) for i in g)
第二次for循環
g = (add(10, i) for i in (add(10, i) for i in g))
"""
res = list(g)
print(res)
#執行結果為:[20,21,22,23], 答案選C
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]
#D. res=[21,22,23,24]
4 yield和return的區別
yield
- 可以返回值(支持多個,並且組織成元組)
- 函數體代碼遇到yield不會結束而是阻塞
- yield可以將函數變成生成器 並且還支持外界傳值
return
- 可以返回值(支持多個並且組織成元組)
- 函數體代碼遇到return直接結束
5 生成器和迭代器總結
迭代器對象 生成器對象 我們都可以看成是"工廠",只有當我們所要數據的時候工廠才會加工出"數據"
主要目的就是節省空間