18、函式遞迴
阿新 • • 發佈:2021-07-02
若函式體包含yield關鍵字,再呼叫函式,並不會執行函式體程式碼,得到的返回值即生成器物件
>>> def my_range(start,stop,step=1):
... print('start...')
... while start < stop:
... yield start
... start+=step
... print('end...')
...
>>> g=my_range(0,3)
>>> g
<generator object my_range at 0x000002283363A970>
生成器內建有iter和next方法,所以生成器本身就是一個迭代器
>>> g.__iter__
<method-wrapper '__iter__' of generator object at 0x000002283363A970>
>>> g.__next__
<method-wrapper '__next__' of generator object at 0x000002283363A970>
因而我們可以用next(生成器)觸發生成器所對應函式的執行
>>> next(g) # 觸發函式執行直到遇到yield則停止,將yield後的值返回,並在當前位置掛起函式
start...
0
>>> next(g) # 再次呼叫next(g),函式從上次暫停的位置繼續執行,知道重新遇到yield...
1
>>> next(g)
2
>>> next(g)
end...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
既然生成器物件屬於迭代器,那麼必然可以使用for迴圈迭代,如下:
>>> for i in countdown(3):
... print(i)
...
countdown start
3
2
1
Done!
有了yield關鍵字,我們就有了一種自定義迭代器的實現方式。yield可以用於返回值,但不同於return,函式一旦遇到return就結束了,而yield可以儲存函式的執行狀態掛起函式,用來返回多次值
二、yield表示式應用
在函式內可以採用表示式形式的yield
>>> def eater():
... print('Ready to eat')
... while True:
... food = yield
... print('get the food:%s,and start to eat' % food)
可以拿到函式的生成器物件持續為函式體send值,如下
>>> g = eater()
>>> g
<generator object eater at 0x00000228335DAB30>
>>> next(g)
Ready to eat
>>> g.send('包子')
get the food:包子,and start to eat
>>> g.send('饅頭')
get the food:饅頭,and start to eat
針對表示式形式的yield,生成器物件必須事先被初始化一次,讓函式掛在food=yield的位置,等待呼叫g.send()為函式體傳值,g.send(None)等同於next(g)
我們可以編寫裝飾器來完成為所有表示式形式yield對應生成器的初始化操作,如下
def init(func):
def wrapper(*args, **kwargs):
g = func(*args, **kwargs)
next(g)
return wrapper
表示式形式的yield也可以用於返回多次值,即變數名=yield值的形式,如下
def init(func):
def wrapper(*args, **kwargs):
g = func(*args, **kwargs)
next(g)
return g
return wrapper
三、三元表示式、列表生成式、生成器表示式
三元表示式
三元表示式是python為我們提供的一種簡化程式碼的解決方案,語法如下
res = 條件成立時返回的值 if 條件 else 條件不成立時返回的值
針對下述場景
def max(x, y):
if x > y:
return x
else:
return y
res = max(1, 2)
用三元表示式可以一行解決
x = 1
y = 2
res = x if x > y else y
列表生成器
列表生成器是python為我們提供的一種簡化程式碼的解決方案,用來快速生成列表,語法如下
[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)
針對下述場景
egg_list=[]
for i in range(10):
egg_list.append('雞蛋%s' %i)
用列表生成器可以一行程式碼解決
>>> ['雞蛋%s' %i for i in range(10)]
['雞蛋0', '雞蛋1', '雞蛋2', '雞蛋3', '雞蛋4', '雞蛋5', '雞蛋6', '雞蛋7', '雞蛋8', '雞蛋9']
生成器表示式
建立一個生成器物件有兩種方式,一種是呼叫帶yield關鍵字的函式,另一種就是生成器表示式,與列表生成器的語法格式相同,只需要將[]切換成(),即:
(expression for item in iterable if condition)
對比列表生成式返回的是一個列表,生成器表示式返回的是一個生成器物件
>>> [x*x for x in range(3)]
[0, 1, 4]
>>> g = (x*x for x in range(3))
>>> g
<generator object <genexpr> at 0x00000206CD15AB30>
對比列表生成式,生成器表示式的優點自然是節省記憶體(一次只產生一個值在記憶體中)
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g) # 丟擲異常StopIteration
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
with open(r'1.txt', 'r', encoding='utf8') as f:
num = (len(line) for line in f) # for迴圈預設每次讀取一行
totle = sum(num) # 依次執行next(num),然後累加到一起得到結果
print(totle)