Python生成器定義與簡單用法實例分析

不言
發布: 2018-05-02 16:04:57
原創
1168 人瀏覽過

這篇文章主要介紹了Python生成器定義與簡單用法,結合實例形式較為詳細的分析了Python生成器的概念、原理、使用方法及相關操作注意事項,需要的朋友可以參考下

本文實例講述了Python生成器定義與簡單用法。分享給大家供大家參考,具體如下:

一、什麼是生成器

在Python中,由於受到記憶體的限制,清單容量肯定是有限的。例如我們建立一個包含一億個元素的列表,Python首先會在記憶體中開闢足夠的空間來儲存這個包含一億個元素的列表,然後才允許用戶去使用這個列表,這可能會導致以下問題:

  1、內存中沒有足夠的內存空間開存儲這個列表,從而導致列表無法創建

  2、即使列表成功創建,然而仍會消耗很長的時間,導致程序效率低下

  3、若用戶只想訪問列表前面的幾個元素,則後面列表絕大多數元素佔用的空間就都白白浪費了

為了有效解決以上的問題,Python中引入了一種「一邊循環,一邊計算」的新機制,即當使用者需要使用某個物件時,Python才根據事先設計好的規則開闢記憶體空間創建這個物件是供使用者使用,而不是像列表一樣事先將所有的物件都創建完畢之後再提供給使用者使用#。這種機制在Python中成為生成器(generator)。

二、生成器的建立

A、產生器推到式

與列表推到式類似,只不過生成器推導式使用()而非[],並且最終返回的是生成器而非列表

g=((i+2)**2 for i in range(2,30)) #g是一个生成器
print(g) #g为空,里面包含任何元素
登入後複製

運行結果:

< ;generator object at 0x0000000002263150>

B、yield關鍵字

在一個函數定義中包含yield關鍵字,則這個函數就不再是一個普通的函數,而是一個生成器(generator)

[說明]:yield指令可以暫停一個函數並返回其中間結果,使用該指令的函數將保存執行環境,並在必要時恢復

def fib(max):
  n,a,b=0,0,1
  while n<max:
    #print(b)
    yield b
    a,b=b,a+b
    n+=1
  return &#39;done&#39;
f=fib(6)
print(f)
登入後複製

運行結果:

[註]:普通函數和變成生成器的函數的差異:

普通函數是順序執行的,遇到return或是最後一行函數語句就回傳。而變成生成器的函數在每次呼叫__next__()方法時執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行

f=fib(6)
print(f)
print(f.__next__())
print(f.__next__())
print(&#39;暂停一下&#39;)
print(f.__next__())
print(f.__next__())
登入後複製

運行結果:

1

1#暫停2

3




# #三、生成器方法(參考:伯樂在線)

###1.close()方法:手動關閉生成器函數,後面的呼叫會直接傳回StopIteration異常##### #
def func():
  yield 1
  yield 2
  yield 3
g=func()
g.__next__()
g.close() #手动关闭生成器
g.__next__() #关闭后,yield 2和yield 3语句将不再起作用
登入後複製
###運行結果:#########Traceback (most recent call last):###  File "E:\py3Demo\Hello\generatorDemo.py", line 9, in ###    g.__next__() #關閉後,yield 2和yield 3語句將不再運作###StopIteration############2.__next__()方法:傳回生成器的下一次呼叫######
def func():
  n=1
  for i in range(3):
    yield n
    n+=1
c=func()
a1=c.__next__()
a2=c.__next__()
a3=c.__next__()
登入後複製
######[流程解釋]:############對於普通的生成器,第一個__next__()方法的呼叫相當於啟動生成器,此時會從生成器函數的第一行開始執行,直到第一次執行完yield語句(第四行)後,跳出生成器函數。 ############當呼叫第二個__next__()方法後,會重新進入生成器函數,並從yield語句的下一語句(第五行)開始執行,直到重新運行到yield語句,執行後再次跳出生成器函數。 ############後面的__next__()方法呼叫以此類推#############3.send()方法:接受外部傳入的一個變量,並根據變數內容計算結果返回生成器函數############[註]:############(1)send()方法和__next__ ()方法相似,差異在於send()方法可以傳遞給yield表達式值,而__next__()方法不能傳遞特定的值,只能傳遞None給yield表達式,因#########此可以將generator.__next__()理解為generator.send(None)##########

(2)第一次调用生成器函数时,必须使用__next__()语句或是send(None),不能使用send发送一个非None的值给生成器函数,否则会出错,因为没有yield语句来接收这个值

def gen():
  value=0
  while True:
    receive=yield value
    if receive==&#39;end&#39;:
      break
    value=&#39;Got:%s&#39; %receive
g=gen()
print(g.__next__()) #或是print(g.send(None)),从而启动生成器
print(g.send(&#39;aaa&#39;))
print(g.send(3))
print(g.send(&#39;end&#39;))
登入後複製

运行结果:

0
Got:aaa
Got:3
Traceback (most recent call last):
File "E:\py3Demo\Hello\generatorDemo.py", line 13, in
print(g.send('end'))
StopIteration

[流程解释]:

a.通过g.send(None)或g.__next__()启动生成器函数,并执行到第一个yield语句结束的位置并将函数挂起。此时执行完了yield语句,但是没有给receive赋值,因此yield value会输出value的初始值0

b.g.send('aaa')先将字符串‘aaa'传入到生成器函数中并赋值给receive,然后从yield语句的下一句重新开始执行函数(第五句),计算出value的值后返回到while头部开始新一轮的循环,执行到yield value语句时停止,此时yield value会输出‘Got:aaa',然后挂起

c.g.send(3)重复步骤b,最后输出结果为‘Got:3'

d.g.send('end')会使程序执行break然后跳出循环,从而函数执行完毕,得到StopIteration异常

4.throw()方法:向生成器发送一个异常。

def gen():
  while True:
    try:
      yield &#39;normal value&#39; #返回中间结果,此处的yield和return的功能相似
      yield &#39;normal value2&#39;
      print(&#39;I am here&#39;)
    except ValueError:
      print(&#39;We got ValueError&#39;)
    except Exception:
      print(&#39;Other errors&#39;)
      break
g=gen()
print(g.__next__())
print(g.throw(ValueError))
print(g.__next__())
print(g.throw(TypeError))
登入後複製

运行结果:

Traceback (most recent call last):
File "E:\py3Demo\Hello\generatorDemo.py", line 17, in
print(g.throw(TypeError))
StopIteration
normal value
We got ValueError
normal value
normal value2
Other errors

[解释]:

a.print(g.__next__())会输出normal value,并停在yield 'normal value2'之前

b.由于执行了g.throw(ValueError),所以回跳过后续的try语句,即yield ‘normal value2'不会执行,然后进入到except语句,打印出‘We got ValueError'。之后再次进入到while语句部分,消耗一个yield,输出normal value

c.print(g.__next__())会执行yield ‘normal value2'语句,并停留在执行完该语句后的位置

d.g.throw(TypeError)会跳出try语句,因此print('I am here')不会被执行,然后打印‘Other errors',并执行break语句跳出while循环,然后到达程序结尾,打印StopIteration异常的信息

四、生成器的运用

import time
def consumer(name):
  print(&#39;%s准备吃包子啦!&#39; %name)
  while True:
    baozi=yield #接收send传的值,并将值赋值给变量baozi
    print(&#39;包子[%s]来了,被[%s]吃了!&#39; %(baozi,name))
def producer(name):
  c1=consumer(&#39;A&#39;) #把函数变成一个生成器
  c2=consumer(&#39;B&#39;)
  c1.__next__()#调用这个方法会走到yield处暂时返回
  c2.__next__()
  print(&#39;开始准备做包子啦!&#39;)
  for i in range(10):
    time.sleep(1)
    print(&#39;做了一个包子,分成两半&#39;)
    c1.send(i)
    c2.send(i)
producer(&#39;Tomwenxing&#39;)
登入後複製

运行结果:

A准备吃包子啦!
B准备吃包子啦!
开始准备做包子啦!
做了一个包子,分成两半
包子[0]来了,被[A]吃了!
包子[0]来了,被[B]吃了!
做了一个包子,分成两半
包子[1]来了,被[A]吃了!
包子[1]来了,被[B]吃了!
做了一个包子,分成两半
包子[2]来了,被[A]吃了!
包子[2]来了,被[B]吃了!
做了一个包子,分成两半
包子[3]来了,被[A]吃了!
包子[3]来了,被[B]吃了!
做了一个包子,分成两半
包子[4]来了,被[A]吃了!
包子[4]来了,被[B]吃了!
做了一个包子,分成两半
包子[5]来了,被[A]吃了!
包子[5]来了,被[B]吃了!
做了一个包子,分成两半
包子[6]来了,被[A]吃了!
包子[6]来了,被[B]吃了!
做了一个包子,分成两半
包子[7]来了,被[A]吃了!
包子[7]来了,被[B]吃了!
做了一个包子,分成两半
包子[8]来了,被[A]吃了!
包子[8]来了,被[B]吃了!
做了一个包子,分成两半
包子[9]来了,被[A]吃了!
包子[9]来了,被[B]吃了!

相关推荐:

Python迭代器定义与简单用法分析

Python装饰器原理与用法分析


以上是Python生成器定義與簡單用法實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!