爲何要使用 Python 生成器?該如何使用 Python 生成器?

自從 PEP 255 引入生成器以來,它就是 Python 中重要的一部分.html

生成器容許你定義一個有迭代器行爲的函數.python

它容許程序猿更快,更簡單而且以一個乾淨的方式建立一個迭代器.api

那麼什麼是迭代器呢,你或許會問?bash

iterator 迭代器是一個能夠被迭代的(循環)對象。它能夠抽象爲一個裝着數據同時有着可迭代對象的行爲的容器。或許你已經天天在使用一些可迭代的對象:諸如字符串,列表,字典或其它名字的對象.微信

一個迭代器是一個實現了迭代器接口 Iterator Protocol 的類。這個接口爲類提供了兩個方法: __iter____next__.函數

嗯~回到上一步。你爲何想要建立一個迭代器呢?學習

節省內存空間

當實例化後,迭代器並不會計算它每個項的值,他們只會等你訪問這些項的時候採起計算。這也就是衆所周知的惰性求值ui

當你有一個很是大的數據集須要計算時,惰性求值是頗有用處的。它容許你立刻就能開始使用數據,儘管整個數據集還在計算中。lua

假設咱們想要得到小於某個最大值的全部素數。spa

咱們先定義一個函數,它能夠檢查一個數字是否爲素數:

def check_prime(number):
    for divisor in range(2, int(number ** 0.5) + 1):
        if number % divisor == 0:
            return False
    return True複製代碼

而後,咱們定義一個迭代器類,包含__iter____next__ 方法。

class Primes:
    def __init__(self, max):
        self.max = max
        self.number = 1
    def __iter__(self):
        return self
    def __next__(self):
        self.number += 1
        if self.number >= self.max:
            raise StopIteration
        elif check_prime(self.number):
            return self.number
        else:
            return self.__next__()複製代碼

Primes 類經過給定一個最大值來實例化。若是下一個素數比最大值 max 還要大,迭代器就會拋出一個 StopIteration 異常來把迭代器停掉。

當咱們請求迭代器中的下一個元素時,它會給 number 加 1 並檢查這個數字是否爲素數。若是不是,它會再次調用__next__直到 number 成爲素數。一旦如此,迭代器就將這個數字返回。

經過使用迭代器,咱們並不會在內存中建立一個包含不少素數的列表。相反,咱們將會在每次請求下一個素數時纔去生成它。

讓咱們來試一試:

primes = Primes(100000000000)
print(primes)
for x in primes:
    print(x)
    ......
<__main__.Primes object at 0x1021834a8>
2
3
5
7
11
...複製代碼

Primes 對象的每一次迭代都調用了 __next__ 來生成下一個素數。

迭代器只能夠被迭代一輪。若是你嘗試再迭代 primes 一輪,它將不會返回任何值,表現得就像個空列表。

既然咱們已經知道了什麼是迭代器,以及怎麼製做一個迭代器,咱們接下來將繼續來看看生成器。

生成器

回想下,生成器函數容許咱們以一種更簡單的方式來建立迭代器。

生成器給 Python 引入了 yield 聲明。它用起來有點像 return,由於它會返回一個值。

區別在於 yield 會保存函數的狀態。在函數下一次被調用時,將會從其離開的地方繼續執行,而且變量值也與它以前執行 yield 操做前相同。

若是把咱們的 Primes 迭代器轉換爲生成器,它看起來會像這樣:

def Primes(max):
    number = 1
    while number < max:
        number += 1
        if check_prime(number):
            yield number
primes = Primes(100000000000)
print(primes)
for x in primes:
    print(x)
......
<generator object Primes at 0x10214de08>
2
3
5
7
11
...複製代碼

如今真是太 pythonic 了!咱們還能再給力點嗎?

固然!咱們可使用 PEP 289 中介紹的生成器表達式。

這至關因而生成器的列表推導式。它用起來與列表推導式相同,不過表達式由 () 包裹而不是 []

下面的表達式能夠代替咱們上面的生成器函數:

primes = (i for i in range(2, 100000000000) if check_prime(i))
print(primes)
for x in primes:
    print(x)
......
<generator object <genexpr> at 0x101868e08>
2
3
5
7
11
...複製代碼

這就是 Python 生成器的美妙之處。

總結...

  • 生成器容許你以一種很是 pythonic 的方式來建立迭代器。
  • 迭代器容許惰性求值,只有在請求下一個元素時迭代器對象纔會去生成它。這對於很是大的數據集是頗有用的。
  • 迭代器和生成器都只能被迭代一輪。
  • 生成器函數比迭代器更好。
  • 生成器表達式比迭代器更好(只在簡單狀況下如此)。
python開發等相關IT技術羣: 887934385 提供資料,部分相關源碼 共同探討

不經常使用q能夠掃如下二維碼添加助理客服小姐姐微信,經過後第一時間回覆

關注微信公衆號「 python社區營 」 第一時間學習更多知識

相關文章
相關標籤/搜索