亚洲区国产区激情区无码区,国产成人mv视频在线观看,国产A毛片AAAAAA,亚洲精品国产首次亮相在线

Python 基礎(chǔ)教程

Python 流程控制

Python 函數(shù)

Python 數(shù)據(jù)類(lèi)型

Python 文件操作

Python 對(duì)象和類(lèi)

Python 日期和時(shí)間

Python 高級(jí)知識(shí)

Python 參考手冊(cè)

Python 生成器

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

Python中的生成器是什么?

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

這既冗長(zhǎng)又違反直覺(jué)。生成器在這種情況下可以派上用場(chǎng)。

Python生成器是創(chuàng)建迭代器的簡(jiǎn)單方法。我們上面提到的所有開(kāi)銷(xiāo)都由Python的生成器自動(dòng)處理。

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

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

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

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

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

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

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

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

  • 調(diào)用時(shí),它返回一個(gè)對(duì)象(迭代器),但不會(huì)立即開(kāi)始執(zhí)行。

  • 像__iter__()和__next__()這樣的方法會(huì)自動(dòng)實(shí)現(xiàn)。因此,我們可以使用next()來(lái)遍歷項(xiàng)目。

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

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

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

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

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

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

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

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

>>> # 它返回一個(gè)對(duì)象,但不立即開(kāi)始執(zhí)行.
>>> a = my_gen()

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

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

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

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

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

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

要重新啟動(dòng)該過(guò)程,我們需要使用= my_gen()之類(lèi)的東西來(lái)創(chuàng)建另一個(gè)生成器對(duì)象。

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

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

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

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

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

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

運(yùn)行該程序時(shí),輸出為:

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

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

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

通常,生成器函數(shù)是通過(guò)具有適當(dāng)終止條件的循環(huán)來(lái)實(shí)現(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í)證明,此生成器函數(shù)不僅適用于字符串,還適用于其他種類(lèi)的可迭代對(duì)象,例如listtuple等。

Python生成器表達(dá)式

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

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

生成器表達(dá)式的語(yǔ)法類(lèi)似于Python中的列表理解語(yǔ)法。但是將方括號(hào)替換為圓括號(hào)。

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

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

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

# 使用列表理解對(duì)每個(gè)項(xiàng)目進(jìn)行平方
# 輸出: [1, 9, 36, 100]
[x**2 for x in my_list]

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

上面我們可以看到生成器表達(dá)式?jīng)]有立即產(chǎn)生所需的結(jié)果。相反,它返回了一個(gè)生成器對(duì)象,該對(duì)象帶有按需生產(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)

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

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

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

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

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

1.易于實(shí)施

與它們的迭代器類(lèi)對(duì)應(yīng)項(xiàng)相比,生成器可以以一種清晰而簡(jiǎn)潔的方式實(shí)現(xiàn)。下面是一個(gè)使用iterator類(lèi)實(shí)現(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

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

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

由于生成器自動(dòng)跟蹤細(xì)節(jié),因此簡(jiǎn)潔明了,實(shí)現(xiàn)起來(lái)也更加簡(jiǎn)潔。

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

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

而這種序列的生成器實(shí)現(xiàn)對(duì)內(nèi)存友好,因此是首選的,因?yàn)樗淮沃荒苌梢豁?xiàng)。

3.表示無(wú)限流

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

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

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

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

生成器可用于流水線化一系列操作。最好用一個(gè)示例來(lái)說(shuō)明。

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

假設(shè)所有內(nèi)容都是字符串,沒(méi)有可用的數(shù)字被標(biāo)記為“ N / A”。生成器的實(shí)現(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))

這種流水線高效且易于閱讀(是的,非常酷?。?/p>