>>gen=(x**2forxinrange(5))>>>genat0x0000000002FB7B40>>>>forgingen:" />

亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

理解python中生成器用法

系統(tǒng) 1582 0

生成器(generator)概念

生成器不會把結(jié)果保存在一個系列中,而是保存生成器的狀態(tài),在每次進行迭代時返回一個值,直到遇到StopIteration異常結(jié)束。

生成器語法

生成器表達式: 通列表解析語法,只不過把列表解析的[]換成()
生成器表達式能做的事情列表解析基本都能處理,只不過在需要處理的序列比較大時,列表解析比較費內(nèi)存。

            
>>> gen = (x**2 for x in range(5))
>>> gen

            
              
                 at 0x0000000002FB7B40>
>>> for g in gen:
...  print(g, end='-')
...
0-1-4-9-16-
>>> for x in [0,1,2,3,4,5]:
...  print(x, end='-')
...
0-1-2-3-4-5-
              
            
          

生成器函數(shù): 在函數(shù)中如果出現(xiàn)了yield關(guān)鍵字,那么該函數(shù)就不再是普通函數(shù),而是生成器函數(shù)。

但是生成器函數(shù)可以生產(chǎn)一個無線的序列,這樣列表根本沒有辦法進行處理。

yield 的作用就是把一個函數(shù)變成一個 generator,帶有 yield 的函數(shù)不再是一個普通函數(shù),Python 解釋器會將其視為一個 generator。

下面為一個可以無窮生產(chǎn)奇數(shù)的生成器函數(shù)。

            
def
odd():
n=1
while
True:
yield
n
n+=2
odd_num
=
odd()
count
=
0
for
o
in
odd_num:
if
count
>=5:
break
print(o)
count
+=1

          

當(dāng)然通過手動編寫迭代器可以實現(xiàn)類似的效果,只不過生成器更加直觀易懂

            
class Iter:
  def __init__(self):
    self.start=-1
  def __iter__(self):
    return self
  def __next__(self):
    self.start +=2 
    return self.start
I = Iter()
for count in range(5):
  print(next(I))
          

題外話: 生成器是包含有__iter()和next__()方法的,所以可以直接使用for來迭代,而沒有包含StopIteration的自編Iter來只能通過手動循環(huán)來迭代

            
>>>
from
collections
import
Iterable
>>>
from
collections
import
Iterator
>>>
isinstance(odd_num,
Iterable)
True
>>>
isinstance(odd_num,
Iterator)
True
>>>
iter(odd_num)
is
odd_num
True
>>>
help(odd_num)
Help
on
generator
object:
odd
=
class
generator(object)
| Methods
defined
here:
|
| __iter__(self,
/)
|   Implement
iter(self).
|
| __next__(self,
/)
|   Implement
next(self).
......
          

到上面的結(jié)果,現(xiàn)在你可以很有信心的按照Iterator的方式進行循環(huán)了吧!

在 for 循環(huán)執(zhí)行時,每次循環(huán)都會執(zhí)行 fab 函數(shù)內(nèi)部的代碼,執(zhí)行到 yield b 時,fab 函數(shù)就返回一個迭代值,下次迭代時,代碼從 yield b 的下一條語句繼續(xù)執(zhí)行,而函數(shù)的本地變量看起來和上次中斷執(zhí)行前是完全一樣的,于是函數(shù)繼續(xù)執(zhí)行,直到再次遇到 yield。看起來就好像一個函數(shù)在正常執(zhí)行的過程中被 yield 中斷了數(shù)次,每次中斷都會通過 yield 返回當(dāng)前的迭代值。

yield 與 return

在一個生成器中,如果沒有return,則默認執(zhí)行到函數(shù)完畢時返回StopIteration;

            
>>> def g1():
...   yield 1
...
>>> g=g1()
>>> next(g)  #第一次調(diào)用next(g)時,會在執(zhí)行完yield語句后掛起,所以此時程序并沒有執(zhí)行結(jié)束。
1
>>> next(g)  #程序試圖從yield語句的下一條語句開始執(zhí)行,發(fā)現(xiàn)已經(jīng)到了結(jié)尾,所以拋出StopIteration異常。
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
StopIteration
>>>
              
            
          

如果遇到return,如果在執(zhí)行過程中 return,則直接拋出 StopIteration 終止迭代。

            
>>>
def
g2():
...  
yield
'a'
...  
return
...  
yield
'b'
...
>>>
g=g2()
>>>
next(g)  #程序停留在執(zhí)行完yield
 'a'語句后的位置。
'a'
>>>
next(g)  #程序發(fā)現(xiàn)下一條語句是return,所以拋出StopIteration異常,這樣yield
 'b'語句永遠也不會執(zhí)行。
Traceback
(most
recent
call
last):
 File
"
            
              ",
line
1,
in

              
                
StopIteration

              
            
          

如果在return后返回一個值,那么這個值為StopIteration異常的說明,不是程序的返回值。

生成器沒有辦法使用return來返回值。

            
>>> def g3():
...   yield 'hello'
...   return 'world'
...
>>> g=g3()
>>> next(g)
'hello'
>>> next(g)
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
StopIteration: world
              
            
          

生成器支持的方法

            
>>>
help(odd_num)
Help
on
generator
object:
odd
=
class
generator(object)
| Methods
defined
here:
......
| close(...)
|   close()
->
raise
GeneratorExit
inside
generator.
|
| send(...)
|   send(arg)
->
send
'arg'
into
generator,
|   return
next
yielded
value
or
raise
StopIteration.
|
| throw(...)
|   throw(typ[,val[,tb]])
->
raise
exception
in
generator,
|   return
next
yielded
value
or
raise
StopIteration.
......

          

close()

手動關(guān)閉生成器函數(shù),后面的調(diào)用會直接返回StopIteration異常。

            
>>> def g4():
...   yield 1
...   yield 2
...   yield 3
...
>>> g=g4()
>>> next(g)
1
>>> g.close()
>>> next(g)  #關(guān)閉后,yield 2和yield 3語句將不再起作用
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
StopIteration
              
            
          

send()

生成器函數(shù)最大的特點是可以接受外部傳入的一個變量,并根據(jù)變量內(nèi)容計算結(jié)果后返回。

這是生成器函數(shù)最難理解的地方,也是最重要的地方,實現(xiàn)后面我會講到的協(xié)程就全靠它了。

            
def
gen():
  value=0
  while
True:
    receive=yield
value
    if
receive=='e':
      break
    value
=
'got: %s'
%
receive
g=gen()
print(g.send(None)) 
print(g.send('aaa'))
print(g.send(3))
print(g.send('e'))
          

執(zhí)行流程:

通過g.send(None)或者next(g)可以啟動生成器函數(shù),并執(zhí)行到第一個yield語句結(jié)束的位置。此時,執(zhí)行完了yield語句,但是沒有給receive賦值。yield value會輸出初始值0注意:在啟動生成器函數(shù)時只能send(None),如果試圖輸入其它的值都會得到錯誤提示信息。

通過g.send(‘a(chǎn)aa'),會傳入aaa,并賦值給receive,然后計算出value的值,并回到while頭部,執(zhí)行yield value語句有停止。此時yield value會輸出”got: aaa”,然后掛起。

通過g.send(3),會重復(fù)第2步,最后輸出結(jié)果為”got: 3″

當(dāng)我們g.send(‘e')時,程序會執(zhí)行break然后推出循環(huán),最后整個函數(shù)執(zhí)行完畢,所以會得到StopIteration異常。

最后的執(zhí)行結(jié)果如下:

            
0
got: aaa
got: 3
Traceback (most recent call last):
File "h.py", line 14, in 
            
              
 print(g.send('e'))
StopIteration
            
          

throw()

用來向生成器函數(shù)送入一個異常,可以結(jié)束系統(tǒng)定義的異常,或者自定義的異常。

throw()后直接跑出異常并結(jié)束程序,或者消耗掉一個yield,或者在沒有下一個yield的時候直接進行到程序的結(jié)尾。

            
def
gen():
  while
True:
    try:
      yield
'normal value'
      yield
'normal value 2'
      print('here')
    except
ValueError:
      print('we
 got ValueError here')
    except
TypeError:
      break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))

          

輸出結(jié)果為:

            
normal value
we got ValueError here
normal value
normal value 2
Traceback (most recent call last):
 File "h.py", line 15, in 
            
              
  print(g.throw(TypeError))
StopIteration
            
          

解釋:

print(next(g)):會輸出normal value,并停留在yield ‘normal value 2'之前。

由于執(zhí)行了g.throw(ValueError),所以會跳過所有后續(xù)的try語句,也就是說yield ‘normal value 2'不會被執(zhí)行,然后進入到except語句,打印出we got ValueError here。然后再次進入到while語句部分,消耗一個yield,所以會輸出normal value。

print(next(g)),會執(zhí)行yield ‘normal value 2'語句,并停留在執(zhí)行完該語句后的位置。

g.throw(TypeError):會跳出try語句,從而print(‘here')不會被執(zhí)行,然后執(zhí)行break語句,跳出while循環(huán),然后到達程序結(jié)尾,所以跑出StopIteration異常。

下面給出一個綜合例子,用來把一個多維列表展開,或者說扁平化多維列表)

            
def
flatten(nested):
  try:
    #如果是字符串,那么手動拋出TypeError。
    if
isinstance(nested,
str):
      raise
TypeError
    for
sublist
in
nested:
      #yield
 flatten(sublist)
      for
element
in
flatten(sublist):
        #yield
 element
        print('got:',
element)
  except
TypeError:
    #print('here')
    yield
nested
L=['aaadf',[1,2,3],2,4,[5,[6,[8,[9]],'ddf'],7]]
for
num
in
flatten(L):
  print(num)
          

如果理解起來有點困難,那么把print語句的注釋打開在進行查看就比較明了了。

總結(jié)

按照鴨子模型理論,生成器就是一種迭代器,可以使用for進行迭代。

第一次執(zhí)行next(generator)時,會執(zhí)行完yield語句后程序進行掛起,所有的參數(shù)和狀態(tài)會進行保存。再一次執(zhí)行next(generator)時,會從掛起的狀態(tài)開始往后執(zhí)行。在遇到程序的結(jié)尾或者遇到StopIteration時,循環(huán)結(jié)束。

可以通過generator.send(arg)來傳入?yún)?shù),這是協(xié)程模型。

可以通過generator.throw(exception)來傳入一個異常。throw語句會消耗掉一個yield。可以通過generator.close()來手動關(guān)閉生成器。

next()等價于send(None)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 老潮湿影院免费体验区 | 欧美日韩中文字幕在线手机版本 | 亚洲一级毛片 | 日日夜夜摸摸 | 中文字幕在线观看日本 | 成人免费观看高清在线毛片 | 中文字幕专区在线亚洲 | 日本亚洲视频 | 色综合久久88一加勒比 | 成人私人影院在线观看网址 | 天天干天天拍天天操 | 九九九热在线精品免费全部 | 呦女亚洲一区精品 | 在线观看日本中文字幕 | 欧美一级免费片 | 九九影院理论片 | 国产精品高清视亚洲乱码 | 日韩亚州 | 人人干人人舔 | 性做久久久久久久久25的美女 | 男女污污视频在线观看 | 玖玖成人 | 深夜a| 92福利网| 久久国产香蕉一区精品 | 日韩51| 在线欧美日韩国产 | 美女做羞羞 | 日本免费一区二区三区看片 | 亚洲视频一区二区三区四区 | 波多野结衣中文字幕一区 | 日本高清中文字幕一区二区三区a | 国内精品久久久久影院日本 | 国产免费不卡v片在线观看 国产免费不卡视频 | 性色生活免费看性大片 | 欧做爰xxxⅹ性欧美图片 | 香蕉视频黄网站 | 久久精视频 | 天天操视频 夜夜 | 日韩一区二区三 | 亚洲自拍小视频 |