一、串列生成式:可以動態生成串列,而不是用固定值給串列賦值,這樣程式會更靈活
def test(i): # 取偶數 if i % 2 == 0: return i # 普通的生成式 list1 = [i * 2 for i in range(10)] print(list1) # 通過一個函式選擇值 list1 = [test(i) for i in range(10)] print(list1)
二、生成器:串列在呼叫之前已經準備好了,如果這個串列的資料量巨大,那么就一直占用記憶體并且效率低,
比如有一個100萬元素的串列,本次回圈只回圈到前面100個,后面的元素沒有用到,有沒有一種
方式可以在呼叫的時候再生成元素呢?這就是生成器,
# (i * 2 for i in range(10000)) 回傳一個生成器的記憶體地址 list3 = (i * 2 for i in range(10000))
注意:生成器不能用“list[0]”這種方式呼叫里面的元素,因為此時元素還沒有生成,只能用for回圈來訪問
# 可以用__next()__方法來取得下一個,取得下一個的同時前面一個元素就消失了,所以生成器只記錄當前位置的元素,不能往后退,往前用__next()__ print(list3.__next__()) print(list3.__next__()) print(list3.__next__())
# 一般都是用回圈來訪問,不會用到__next__()方法 # 這種方式就是使用了python內置的迭代器,里面內置有next方法 for i in list3 print(i)
舉個例子,將斐波那契數列用生成器來生成,(斐波那契數列定義:除了第一個和第二個數,后面的數都是前面兩個數字想加得到)
下面普通寫法
list_num = (i for i in range(10)) list_new_num = [] for i in list_num: if i != 0 and i != 1: list_new_num.append(list_new_num[i - 1] + list_new_num[i - 2]) else: list_new_num.append(i) print(list_new_num[i])
斐波那契數列的另外一種寫法
n, a, b = 0, 0, 1 # 連續賦值 while n < 10: print(b) a, b = b, a + b n = n + 1
將上面的代碼改一下,
通過yield關鍵詞保存函式的中斷狀態,外面呼叫的代碼可以先執行,不需要等到所有值都計算出來了再執行,這樣可以大大提高效率
執行到next()的時候再回傳yield的位置繼續執行函式內部的代碼,
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 加上yield就變成一個生成器了,相當于保存了b變數此處的值,這個b是要傳到外面的,所以用yield標記
a,b = b,a+b
n = n+1
return "出錯了" # 這是錯誤資訊,將會被例外捕獲,
# 回傳一個生成器,所以fib相當于這個生成器的具體實作,
g = fib(6)
while True:
try:
# 這個next是一個內置方法,作用跟__next()__一樣,取出生成器的下一個元素
# next后進入上面fib函式的yield的位置
x = next(g)
print("g:",x)
except StopIteration as e: # 這里捕捉例外來結束程式
print("error:",e.value)
break
yield 還可以用做單執行緒下的協程的呼叫,雖然仍然是單執行緒(執行緒和協程將在后面單獨介紹),但是通過yield能大大提高效率
以下是一個典型的生產消費模型,一個人生產,若干人消費,而且是同時進行,也是利用了yield的特性,我們稱為“并行生成器”
import time def consumer(name): print("%s準備吃包子了!"%name) while True: baozi = yield # yield無回傳值,為什么賦值給baozhi呢? print("包子[%s]來了,被[%s]吃了," % (baozi,name)) # c = consumer("tangwei") # c.__next__() # 第一個__next__執行到consumer函式中的yield的位置,然后回傳 # c.__next__() # 第二個__next__繼續執行剩下的部分一直到回圈繼續來執行到yield跳出 # c.__next__() # 第一個__next__執行到consumer函式中的yield的位置,然后回傳 # c.send("肉餡") # send將一個值傳遞給consumer函式內的yield的位置,也就是賦值給變數baozi然后next,僅呼叫__next__不會給yield傳遞值 def producer(name): # 生成1個生成器 c1 = consumer("tangwei") # 生成1個生成器 c2 = consumer("chenyadan") # c1準備吃包子,__next__執行到consumer函式中的yield的位置,然后回傳 c1.__next__() # C2同上 c2.__next__() print("我是廚師[%s],開始做包子了..."%(name)) for i in range(10): time.sleep(1) print("做了一個包子,一人一半..") c1.send(i) # send將一個值傳遞給consumer函式內的yield的位置,也就是賦值給變數baozi,僅呼叫__next__不會給yield傳遞值 c2.send(i) # 雖然程式依然是順序執行的,但是因為程式流可以在函式之間跳轉,就無需等到一個函式結束以后再執行另外一個函式,這樣大大提高了執行效率, # 這是單執行緒下的并行效果,也就是協程,協程是比執行緒更小的單位,寄生在執行緒中, producer("狗不理")
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/160775.html
標籤:Python
