1. 程式人生 > >python基礎學習筆記——生成器與推導式

python基礎學習筆記——生成器與推導式

生成器

首先我們來看看什麼是個生成器,生成器本質就是迭代器

在python中有三種方式來獲取生成器

  1.通過生成器函式

  2.通過各種推到式來實現生成器

  3.通過資料的轉換也可以獲取生成器

首先,我們先看一個很簡單的函式:

1 2 3 4 5 6 7 8 9 10 11 def  func():        print ( 11 )      return  22   ret 
=  func() print (ret)   # 執行結果: 11 22

將函式中的return換成yield就是生成器

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 函式 def func():        print( '這是函式func' )        return  '函式func'   func()   # 生成器 def func1():        print( '這是函式func1' )        yield  '函式func'   func1()

執行的結果和上面的不一樣,為什麼呢?? 由於函式中存在yield,那麼這個函式就是一個生成器函式.

1 2 3 4 5 6 7 8 9 def func1():        print( '這是函式func1' )        yield  '函式func'   print(func1())   結果:<generator  object  func1 at 0x0000023B3F280B48>

我們在執行這個函式的時候.就不再是函式的執行了.而是獲取這個生成器.如何使用???

想想迭代器,生成器的本質就是迭代器.所以我們可以直接執行__next__()來執行以下生成器

1 2 3 4 5 6 7 8 9 def  func():       print ( "111" )       yield  222 gener  =  func()  # 這個時候函式不會執⾏. ⽽是獲取到⽣成器 ret  =  gener.__next__()  # 這個時候函式才會執⾏. yield的作⽤和return⼀樣. 也是返回資料 print (ret) 結果: 111 222

那麼我們可以看到,yield和return的效果是一樣的,但是還是有點區別

  yield是分段來執行一個函式

  return是直接停止這個函式

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def  func():      print ( "111" )      yield  222      print ( "333" )      yield  444     gener  =  func() ret  =  gener.__next__() print (ret) ret2  =  gener.__next__() print (ret2) ret3  =  gener.__next__() # 最後⼀個yield執⾏完畢. 再次__next__()程式報錯 print (ret3)   結果: 111 222 333 444

當程式執行完最後一個yield,那麼後面繼續執行__next__()程式會報錯

好了生成器我們說完了.生成器有什麼作用呢?

我們來看一下這個需求,老男孩向樓下賣包子的老闆訂購了10000個包子.包子鋪老闆實在一下就全部都做出來了  

1 2 3 4 5 6 7 8 9 10 def  eat():        lst  =  []        for  in  range ( 1 , 10000 ):          lst.append( '包子' + str (i))      return  lst   =  eat() print (e)

這樣做是沒有問題但是我們目前這麼點人吃不完這麼多,只能先放到一個地方,要是能夠我吃一個老闆做一個就完美了.

1 2 3 4 5 6 7 8 9 10 def  eat():      for  in  range ( 1 , 10000 ):          yield  '包子' + str (i) =  eat() print (e.__next__()) print (e.__next__()) print (e.__next__()) print (e.__next__()) print (e.__next__()) print (e.__next__())

上下的區別: 第一種是直接把包子都拿來,很佔記憶體也就是很佔咱們的位置,第二種使用生成器,想吃就拿一個.吃多少個包多少個.生成器是一個一個的,一直向下進行,不能向上.__next__()到哪,指標就指到哪兒.下一次繼續就獲取指標指向的值

接下來我們再來認識一個新的東西,send方法

send和__next__()一樣都可以讓生成器執行到下一個yield

1 2 3 4 5 6 7 8 9 10 11 def  eat():      for  in  range ( 1 , 10000 ):          =  yield  '包子' + str (i)          print ( 'a is' ,a)            =  yield  '窩窩頭'          print ( 'b is' , b) =  eat() print (e.__next__()) print (e.send( '大蔥' )) print (e.send( '大蒜' ))

send和__next__()區別:

send 和 next()都是讓生成器向下走一次

send可以給上一個yield的位置傳遞值,不能給最後一個yield傳送值,在第一次執行生成器的時候不能使用send()

第一次呼叫的時候使用send()也可以但是send的引數必須是None

1 2 3 4 5 6 7 8 9 def func1():      print( '這是函式func1' )      f1 = yield  '你好'      print(f1)      f2 = yield  '我好'      print(f2) f = func1() f.__next__() f.send( '大家好' )

生成器可以for迴圈來迴圈獲取內部元素:

1 2 3 4 5 6 7 8 9 10 11 def  func():        yield  1      yield  2      yield  3      yield  4      yield  5   =  func() for  in  f:      print (i)
回到頂部

yield from

在python3中提供一種可以直接把可迭代物件中的每一個數據作為生成器的結果進行返回

1 2 3 4 5 6 def func():      lst = [ '衛龍' , '老冰棍' , '北冰洋' , '牛羊配' ]      yield  from  lst g = func() for  in  g:      print(i)

有個小坑,yield from 是將列表中的每一個元素返回,所以 如果寫兩個yield from 並不會產生交替的效果

1 2 3 4 5 6 7 8 9 def func():      lst1 = [ '衛龍' , '老冰棍' , '北冰洋' , '牛羊配' ]      lst2 = [ '饅頭' , '花捲' , '豆包' , '大餅' ]      yield  from  lst1      yield  from  lst2       g = func() for  in  g:      print(i)
回到頂部

推導式

列表推導式

列表推導式生成器表示式以及其他推導式,首先我們先看一下這樣的程式碼,給出一個列表,通過迴圈,想列表中新增1~10:

1 2 3 4 5 li  =  [] for  in  range ( 10 ):      li.append(i)   print (li)

我們換成列表推導式是什麼樣的,來看看:

列表推導式的常⽤寫法:  

[結果 for 變數 in 可迭代物件] 

1 2 ls  =  [i  for  in  range ( 10 )] print (ls)

列表推導式是通過⼀行來構建你要的列表, 列表推導式看起來程式碼簡單. 但是出現錯誤之  

後很難排查.   

例. 從python1期到python17期寫入列表lst:

1 2 lst  =  [ 'python%s'  %  for  in  range ( 1 , 18 )] print (lst)

篩選模式

[結果 for 變數 in 可迭代物件 if 條件]

1 2 3 print([i  for  in  range(10)  if  i > 3]) 結果: [4, 5, 6, 7, 8, 9]

生成器表示式  

這個其實就將列表推導式倆邊的中括號換成小括號就可以了,我們來看一下

1 2 3 4 5 6 7 8 l = (i  for  in  range(10)) print(l)    print(l.__next__()) print(l.__next__()) print(l.__next__()) print(l.__next__()) print(l.__next__())

print(l)的時候獲取到是:

1 2 3 4 5 6 <generator  object  <genexpr> at 0x000001D8C7570B48> 0 1 2 3 4

生成器表示式也可以進行篩選

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # 獲取1-100內能被3整除的數 gen = (i  for  in  range(1,100)  if  i % 3 == 0) for  num  in  gen:      print(num)     # 100以內能被3整除的數的平⽅ gen = (i * i  for  in  range(100)  if  i % 3 == 0) for  num  in  gen:      print(num)     # 尋找名字中帶有兩個e的人的名字 names = [[ 'Tom' 'Billy' 'Jefferson' 'Andrew' 'Wesley' 'Steven' 'Joe' ],           [ 'Alice' 'Jill' 'Ana' 'Wendy' 'Jennifer' 'Sherry' 'Eva' ]]     # 不用推導式和表示式 result = [] for  first  in  names:      for  name  in  first:          if  name.count( "e" ) >= 2:              result.append(name) print(result)   # 推導式 gen = (name  for  first  in  names  for  name  in  first  if  name.count( 'e' ) >= 2)   for  in  gen:      print(i)

生成器表示式和列表推導式的區別:

1. 列表推導式比較耗記憶體,一次性載入.生成器表示式幾乎不佔用記憶體.使用的時候才分配和使用記憶體

2. 得到的值不一樣,列表推導式得到的是一個列表.生成器表示式獲取的是一個生成器

舉個例子:

李大錘想吃雞蛋就上街買了一籃子的雞蛋放家裡,吃的時候拿一個吃的時候拿一個,這樣就是一個列表推導式,一次性拿夠佔地方.

王二麻子也想吃雞蛋,他上街卻買了一隻母雞回家.等