python中的生成器(一)

咱們先考慮一個場景:python

  有個情景須要循環輸出1——10.算法

  這裏給兩種方法:緩存

list1 = [1,2,3,4,5,6,7,8,9,10]
for i in list1:
    print(i)
for i in range(1,11):
    print(i)

兩種方式輸出結果同樣,可是咱們考慮一下,若是要求輸出1——1000000呢?函數

第一種方式會致使list1裏面真實放入1000000長度的數字,佔用空間很大,明顯不是明智之舉,spa

再來看第二種方法,用到range幫助咱們生成數據,在python3中range的本質就是一個生成器。.net

在python2中:range返回的是一個等差列表,好比[0,1,2,3,4,5,6,7,```````], 而xrange纔是返回一個生成器對象. 即python2 range()==[```````````````````], python2 xrange()==python3 range()code

具體對比查看:https://blog.csdn.net/humanking7/article/details/45950967對象

(一)這裏寫一個函數,在生成器函數的名稱中加上gen 前綴或後綴,不過這不是必要的習慣:blog

1 def gen_create_range(start,end):
2 
3     while start < end:
4         yield start
5         start += 1
6 
7 for i in gen_create_range(1,5):
8     print(i)

 #output:generator

       1

    2

    3

    4

  這個函數沒有return 可是能夠有返回值,注意看裏面有個yield關鍵字,這個函數和range()函數很像。

  (二)什麼是生成器:函數定義中包含yield關鍵字那麼函數就變成了生成器

  概念:若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator   

  生成器表達式: 通列表解析語法,只不過把列表解析的[]換成()  

g= (x**2 for x in range(5))

print g

>> 
<generator object <genexpr> at 0x0000000002771798>

 

#若是、
L=[x**2 for x in range(5)]

print L 

>>
[0, 1, 4, 9, 16]

  也就是說:建立L和g的區別僅在於最外層的[]和(),L是一個list,而g是一個generator

1).函數中只要出現了yield語句就會將其轉變成一個生成器函數

  • 特別之處在於,yield 的做用就是把一個函數變成一個 generator,帶有 yield 的函數再也不是一個普通函數,Python 解釋器會將其視爲一個 generator

  • 與普通函數不同,生成器值會在迭代操做的時候才能運行.yiled能夠把函數中斷,保存狀態和繼續執行的能力

比如一個武打片裏面的慢鏡頭回放,yield把函數裏面你要保存的值中斷並保存,你經過調用next()來回放

 

好比:

def countdown(n):
   print('Starting to count from',n)
   while n>0:
      yield n
      n-=1
   print('done')

c=countdown(3)
print(c)
>>
<generator object countdown at 0x0000000002821828>
#表示這是一個生成器

 

 2).調用該generator時,首先要生成一個generator對象,而後用next()函數不斷得到下一個返回值

好比:

c=countdown(3)

#run the first yield and emit a value

print(next(c))

>>

Starting to count from 3

3

#run the next yield

print(next(c))

>>

2

#run the next yield

print(next(c))

>>

1

#run the next yield

print(next(c))

>>

done

    print next(c)

StopIteration

 

深刻解釋:

你把yield想象成時間斷點,運行一次next就回放一下,看起來就好像一個函數在正常執行的過程當中被 yield 中斷了數次,每次中斷都會經過 yield 返回當前的迭代值

 

  • 第一次next()是打印了 print('Starting to count from',n),提取第一次的保存值是3

     

  • 第二次再運行next()是繼續在while裏面的斷點接着走,因此沒有打印print('Starting to count from',n), 而是直接提取第二次的保存值2

     

  • 第三次再運行next()是繼續在while裏面的斷點接着走,因此直接輸出1

     

  • 第四次再運行next()的時候,發現yield緩存的武打片慢鏡頭都已經放完了,因此輸出done以後,報了個錯StopIteration

 

3).生成器函數用for循環

for n in countdown(6):
  
  print(n)

>>

Starting to count from 6

6

5

4

3

2

1

done

 

 

正確的方法是使用for循環,由於generator也是可迭代對象

4yield 與 return相愛相殺

1).在一個生成器中,若是沒有return,則默認執行到函數完畢時返回StopIteration

def gen1():
    yield 100

g1=gen1()
print(next(g1))
>>100

第一次調用next(g1)時,會在執行完yield語句後掛起,因此此時程序並無執行結束。

print(next(g1))
>>
Traceback (most recent call last):
  File "C:/about_gen.py", line 71, in <module>
    print(next(g1))
StopIteration

程序試圖從yield語句的下一條語句開始執行,發現已經到告終尾,因此拋出StopIteration異常

 

2).若是遇到return,若是在執行過程當中 return,則直接拋出 StopIteration 終止迭代

def gen2():
    yield 200
    return 
    yield 300

g2=gen2()
print(next(g2))
>>200
# 程序停留在執行完yield 200語句後的位置

print(next(g2))
>>
  File "C:/about_gen.py", line 82, in <module>
    print(next(g2))
StopIteration

 

程序發現下一條語句是return,因此拋出StopIteration異常,這樣yield 'b'語句永遠也不會執行

生成器這個概念一開始很難理解,有點古怪,可是時間久了才知道他的妙用

另外生成器函數是沒有辦法使用return來返回值

相關文章
相關標籤/搜索