Python生成器

生成器

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

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

  1.經過生成器函數app

  2.經過各類推到式來實現生成器函數

  3.經過數據的轉換也能夠獲取生成器spa

首先,咱們先看一個很簡單的函數:指針

def func():
 
    print(11)
    return 22
 
ret = func()
print(ret)
 
# 運行結果:
11
22

 

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

# 函數
def func():
 
     print( '這是函數func' )
 
     return '函數func'
 
func()
 
# 生成器
def func1():
 
     print( '這是函數func1' )
 
     yield '函數func'
 
func1()

運行的結果和上面的不同,爲何呢?? 因爲函數中存在yield,那麼這個函數就是一個生成器函數.對象

def func1():
 
     print( '這是函數func1' )
 
     yield '函數func'
 
print(func1())
 
結果:<generator object func1 at 0x0000023B3F280B48>

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

想一想迭代器,生成器的本質就是迭代器.因此咱們能夠直接執行__next__()來執行如下生成器內存

def func():
      print ( "111" )
      yield 222
gener = func() # 這個時候函數不會執⾏. ⽽是獲取到⽣成器
ret = gener.__next__() # 這個時候函數纔會執⾏. yield的做⽤和return⼀樣. 也是返回數據
print (ret)
結果:
111
222

那麼咱們能夠看到,yield和return的效果是同樣的,可是仍是有點區別

  yield是分段來執行一個函數

  return是直接中止這個函數

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個包子.包子鋪老闆實在一下就所有都作出來了  

def eat():
 
     lst = []
 
     for i in range ( 1 , 10000 ):
         lst.append( '包子' + str (i))
     return lst
 
e = eat()
print (e)

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

def eat():
     for i in range ( 1 , 10000 ):
         yield '包子' + str (i)
e = eat()
print (e.__next__())
print (e.__next__())
print (e.__next__())
print (e.__next__())
print (e.__next__())
print (e.__next__())

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

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

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

def eat():
     for i in range ( 1 , 10000 ):
         a = yield '包子' + str (i)
         print ( 'a is' ,a)
 
         b = yield '窩窩頭'
         print ( 'b is' , b)
e = eat()
print (e.__next__())
print (e.send( '大蔥' ))
print (e.send( '大蒜' ))

send和__next__()區別:

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

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

第一次調用的時候使用send()也能夠可是send的參數必須是None

def func1():
     print( '這是函數func1' )
     f1 = yield '你好'
     print(f1)
     f2 = yield '我好'
     print(f2)
f = func1()
f.__next__()
f.send( '你們好' )

生成器能夠for循環來循環獲取內部元素:

def func():
 
     yield 1
     yield 2
     yield 3
     yield 4
     yield 5
 
f = func()
for i in f:
     print (i)
 

yield from

在python3中提供一種能夠直接把可迭代對象中的每個數據做爲生成器的結果進行返回

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

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

def func():
     lst1 = [ '衛龍' , '老冰棍' , '北冰洋' , '牛羊配' ]
     lst2 = [ '饅頭' , '花捲' , '豆包' , '大餅' ]
     yield from lst1
     yield from lst2
     
g = func()
for i in g:
     print(i)
 

推導式

列表推導式

列表推導式生成器表達式以及其餘推導式,首先咱們先看一下這樣的代碼,給出一個列表,經過循環,想列表中添加1~10:

li = []
for i in range ( 10 ):
     li.append(i)
 
print (li)

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

列表推導式的常⽤寫法:  

[結果 for 變量 in 可迭代對象] 

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

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

後很難排查.   

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

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

篩選模式

[結果 for 變量 in 可迭代對象 if 條件]

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

生成器表達式  

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

l = (i for i in range(10))
print(l) 
 
print(l.__next__())
print(l.__next__())
print(l.__next__())
print(l.__next__())
print(l.__next__())

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

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

生成器表達式也能夠進行篩選

# 獲取1-100內能被3整除的數
gen = (i for i in range(1,100) if i % 3 == 0)
for num in gen:
     print(num)
 
 
# 100之內能被3整除的數的平⽅
gen = (i * i for i 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 i in gen:
     print(i)

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

1. 列表推導式比較耗內存,一次性加載.生成器表達式幾乎不佔用內存.使用的時候才分配和使用內存

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

舉個例子:

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

王二麻子也想吃雞蛋,他上街卻買了一隻母雞回家.等他想吃的時候就讓母雞給下雞蛋,這樣就是一個生成器.須要就給你下雞蛋

生成器的惰性機制: 生成器只有在訪問的時候才取值,說白了.你找他要纔給你值.不找他要.他是不會執行的.

def func():
     print(111)
     yield 222
g = func()  # 生成器g
 
g1 = (i for i in g) # 生成器g1. 可是g1的數據來源於g
 
g2 = (i for i in g1)    # 生成器g2. 來源g1
 
print(list(g))   # 獲取g中的數據. 這時func()纔會被執行. 打印111.獲取到222. g完畢.
print(list(g1))  # 獲取g1中的數據. g1的數據來源是g. 可是g已經取完了. g1 也就沒有數據了
print(list(g2))  # 和g1同理理
 
 
print(next(g))
print(next(g1))
print(next(g2))   # 能夠用next來驗證  其實list就是將內容迭代了轉換成了列表 

這是坑,必定要注意,生成器是要值的時候才能拿值,否則就沒有啦

字典推導式

根據名字應該也能猜到,推到出來的是字典

lst1 = [ 'jay' , 'jj' , 'meet' ]
lst2 = [ '周杰倫' , '林俊杰' , '郭寶元' ]
dic = {lst1[i]:lst2[i] for i in range(len(lst1))}
print(dic)

集合推導式

集合推導式能夠幫咱們直接生成一個集合,集合的特色;無序,不重複 因此集合推導式自帶去重功能

lst = [1,2,3,-1,-3,-7,9]
s = {abs(i) for i in lst}
print(s)

總結:

    推導式有, 列表推導式, 字典推導式, 集合推導式, 沒有元組推導式    

    生成器表達式: (結果 for 變量量 in 可迭代對象 if 條件篩選)    

    生成器表達式能夠直接獲取到⽣成器對象. ⽣成器對象能夠直接進行for循環. ⽣成器具備惰性機制. 

一個面試題,難度係數99999999課星

def add(a, b):
     return a + b
def test():
     for r_i in range(4):
         yield r_i
g = test()
for n in [2, 10]:
     g = (add(n, i) for i in g)
print(list(g))

友情提示: 惰性機制,不到最後不會拿值

相關文章
相關標籤/搜索