Python 基礎(chǔ)教程

Python 流程控制

Python 函數(shù)

Python 數(shù)據(jù)類型

Python 文件操作

Python 對象和類

Python 日期和時間

Python 高級知識

Python 參考手冊

Python 生成器

在本文中,您將學(xué)習(xí)如何使用Python生成器輕松創(chuàng)建迭代,它與迭代器和常規(guī)函數(shù)有何不同,以及為什么要使用它。

Python中的生成器是什么?

用Python構(gòu)建迭代器有很多開銷; 我們必須使用__iter__()和__next__()方法實現(xiàn)一個類,跟蹤內(nèi)部狀態(tài),在沒有要返回的值時觸發(fā)StopIteration等等。

這既冗長又違反直覺。生成器在這種情況下可以派上用場。

Python生成器是創(chuàng)建迭代器的簡單方法。我們上面提到的所有開銷都由Python的生成器自動處理。

簡而言之,生成器是一個函數(shù),它返回一個對象(迭代器),我們可以對其進行迭代(一次一個值)。

如何在Python中創(chuàng)建生成器?

在Python中創(chuàng)建生成器非常簡單。 就像使用yield語句而不是return語句定義普通函數(shù)一樣容易。

如果一個函數(shù)包含至少一個yield語句(它可能包含其他yield或return語句),那么它就成為一個生成器函數(shù)。yield和return都將從函數(shù)返回一些值。

不同之處在于,當(dāng)return語句完全終止一個函數(shù)時,yield語句會暫停該函數(shù)保存其所有狀態(tài),然后在后續(xù)調(diào)用時繼續(xù)執(zhí)行。

生成器函數(shù)與常規(guī)函數(shù)之間的區(qū)別

這是生成器函數(shù)與常規(guī)函數(shù)的不同之處。

  • 生成器函數(shù)包含一個或多個yield語句。

  • 調(diào)用時,它返回一個對象(迭代器),但不會立即開始執(zhí)行。

  • 像__iter__()和__next__()這樣的方法會自動實現(xiàn)。因此,我們可以使用next()來遍歷項目。

  • 一旦函數(shù)產(chǎn)生了結(jié)果,函數(shù)就會暫停,控制就會轉(zhuǎn)移給調(diào)用者。

  • 局部變量及其狀態(tài)在連續(xù)調(diào)用之間被記住。

  • 最后,當(dāng)函數(shù)終止時,在進一步調(diào)用時會自動引發(fā)StopIteration。

這是一個示例,用于說明上述所有要點。我們有一個my_gen()由幾個yield語句命名的生成器函數(shù)。

# 一個簡單的生成器函數(shù)
def my_gen():
    n = 1
    print('這是第一次打印')
    # 生成器函數(shù)包含yield語句
    yield n

    n += 1
    print('這是第二次打印')
    yield n

    n += 1
    print('這是最后一次打印')
    yield n

解釋器中的交互式運行如下所示。在Python Shell中運行這些命令以查看輸出。

>>> # 它返回一個對象,但不立即開始執(zhí)行.
>>> a = my_gen()

>>> # 我們可以使用next()遍歷這些項.
>>> next(a)
這是第一次打印
1
>>> # 一旦函數(shù)產(chǎn)生了結(jié)果,函數(shù)就會暫停,控制就會轉(zhuǎn)移給調(diào)用者。

>>> # 局部變量及其狀態(tài)在連續(xù)調(diào)用之間被記住。
>>> next(a)
這是第二次打印
2

>>> next(a)
這是最后一次打印
3

>>> # 最后,當(dāng)函數(shù)終止時,在進一步調(diào)用時將自動引發(fā)StopIteration。
>>> next(a)
Traceback (most recent call last):
...
StopIteration
>>> next(a)
Traceback (most recent call last):
...
StopIteration

在上面的示例中要注意的一件有趣的事情是,每次調(diào)用之間都會記住變量n的值。

與普通函數(shù)不同,局部變量在函數(shù)產(chǎn)生時不會被破壞。此外,生成器對象只能迭代一次。

要重新啟動該過程,我們需要使用= my_gen()之類的東西來創(chuàng)建另一個生成器對象。

注意:最后要注意的一點是,我們可以直接將生成器與for循環(huán)一起使用。

這是因為,for循環(huán)接受一個迭代器,并使用next()函數(shù)對其進行迭代。當(dāng)StopIteration被觸發(fā)時,它會自動結(jié)束。了解如何在Python中實際實現(xiàn)for循環(huán)。

# 一個簡單的生成器函數(shù)
def my_gen():
    n = 1
    print('這是第一次打印')
    # 生成器函數(shù)包含yield語句
    yield n

    n += 1
    print('這是第二次打印')
    yield n

    n += 1
    print('這是最后一次打印')
    yield n

# 使用for循環(huán)
for item in my_gen():
    print(item)

運行該程序時,輸出為:

這是第一次打印
1
這是第二次打印
2
這是最后一次打印
3

帶有循環(huán)的Python生成器

上面的示例用處不大,我們研究它只是為了了解背景中發(fā)生的事情。

通常,生成器函數(shù)是通過具有適當(dāng)終止條件的循環(huán)來實現(xiàn)的。

讓我們以反轉(zhuǎn)字符串的生成器為例。

def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1,-1,-1):
        yield my_str[i]

# For循環(huán)以反轉(zhuǎn)字符串
# 輸出:
# o
# l
# l
# e
# h
for char in rev_str("hello"):
     print(char)

在此示例中,我們使用range()函數(shù)使用for循環(huán)以相反的順序獲取索引。

事實證明,此生成器函數(shù)不僅適用于字符串,還適用于其他種類的可迭代對象,例如list,tuple等。

Python生成器表達式

使用生成器表達式可以輕松地動態(tài)創(chuàng)建簡單的生成器。它使建造生成器變得容易。

與lambda函數(shù)創(chuàng)建匿名函數(shù)相同,生成器表達式創(chuàng)建匿名生成器函數(shù)。

生成器表達式的語法類似于Python中的列表理解語法。但是將方括號替換為圓括號。

列表理解與生成器表達式之間的主要區(qū)別在于,雖然列表理解生成整個列表,但生成器表達式一次生成一個項目。

他們有點懶,只在需要時才生成項目。由于這個原因,生成器表達式比等價的列表理解的內(nèi)存效率要高得多。

# 初始化列表
my_list = [1, 3, 6, 10]

# 使用列表理解對每個項目進行平方
# 輸出: [1, 9, 36, 100]
[x**2 for x in my_list]

# 同樣的事情可以使用生成器表達式來完成
# 輸出: <generator object <genexpr> at 0x0000000002EBDAF8>
(x**2 for x in my_list)

上面我們可以看到生成器表達式?jīng)]有立即產(chǎn)生所需的結(jié)果。相反,它返回了一個生成器對象,該對象帶有按需生產(chǎn)的物品。

# 初始化list
my_list = [1, 3, 6, 10]

a = (x**2 for x in my_list)
# 輸出: 1
print(next(a))

# 輸出: 9
print(next(a))

# 輸出: 36
print(next(a))

# 輸出: 100
print(next(a))

# 輸出: StopIteration
next(a)

生成器表達式可以在函數(shù)內(nèi)部使用。以這種方式使用時,可以刪除圓括號。

>>> sum(x**2 for x in my_list)
146

>>> max(x**2 for x in my_list)
100

為什么在Python中使用生成器?

有幾個原因使生成器成為一個有吸引力的實現(xiàn)。

1.易于實施

與它們的迭代器類對應(yīng)項相比,生成器可以以一種清晰而簡潔的方式實現(xiàn)。下面是一個使用iterator類實現(xiàn)2的冪序列的示例。

class PowTwo:
    def __init__(self, max = 0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n > self.max:
            raise StopIteration

        result = 2 ** self.n
        self.n += 1
        return result

這代碼很長?,F(xiàn)在,使用生成器函數(shù)執(zhí)行相同的操作。

def PowTwoGen(max = 0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1

由于生成器自動跟蹤細節(jié),因此簡潔明了,實現(xiàn)起來也更加簡潔。

2.節(jié)省內(nèi)存

一個普通的返回序列的函數(shù)會在返回結(jié)果之前在內(nèi)存中創(chuàng)建整個序列。如果序列中的項目數(shù)量很大,會影響效率。

而這種序列的生成器實現(xiàn)對內(nèi)存友好,因此是首選的,因為它一次只能生成一項。

3.表示無限流

生成器是表示無限數(shù)據(jù)流的絕佳媒介。無限流無法存儲在內(nèi)存中,并且由于生成器一次只生成一項,因此它可以表示無限數(shù)據(jù)流。

下面的示例可以生成所有偶數(shù)(至少在理論上)。

def all_even():
    n = 0
    while True:
        yield n
        n += 2

4.流水線發(fā)生器

生成器可用于流水線化一系列操作。最好用一個示例來說明。

假設(shè)我們有一個著名的快餐連鎖店的日志文件。日志文件中有一個列(第4列),該列跟蹤每小時售出的比薩的數(shù)量,我們希望將其求和以得出5年內(nèi)售出的比薩的總數(shù)。

假設(shè)所有內(nèi)容都是字符串,沒有可用的數(shù)字被標(biāo)記為“ N / A”。生成器的實現(xiàn)可以如下。

with open('sells.log') as file:
    pizza_col = (line[3] for line in file)
    per_hour = (int(x) for x in pizza_col if x != 'N/A')
    print("Total pizzas sold = ",sum(per_hour))

這種流水線高效且易于閱讀(是的,非??幔。?。

丰满人妻一级特黄a大片,午夜无码免费福利一级,欧美亚洲精品在线,国产婷婷成人久久Av免费高清