电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

五分鐘帶你搞懂python 迭代器與生成器

瀏覽:6日期:2022-07-12 18:17:15

前言

大家周末好,今天給大家?guī)淼氖荘ython當(dāng)中生成器和迭代器的使用。

我當(dāng)初第一次學(xué)到迭代器和生成器的時候,并沒有太在意,只是覺得這是一種新的獲取數(shù)據(jù)的方法。對于獲取數(shù)據(jù)的方法而言,我們會一種就足夠了。但是在我后來Python的使用以及TensorFlow等學(xué)習(xí)使用當(dāng)中,我發(fā)現(xiàn)很多地方都用到了迭代器和生成器,或者是直接使用,或者是借鑒了思路。今天就讓我們仔細(xì)來看看,它們到底是怎么回事。

迭代器

我們先從迭代器開始入手,迭代器并不是Python獨有的概念,在C++和Java當(dāng)中都有iterator的概念,兩者的使用也都差不多。迭代器主要解決了一個問題,在一個復(fù)雜場景下,獲取數(shù)據(jù)怎么盡可能簡便。

我們來假設(shè)一個場景,假設(shè)我們從某個數(shù)據(jù)源獲取了一批數(shù)據(jù)。然后我們需要調(diào)用前一萬條生成一個結(jié)果,得到結(jié)果之后,我們要將剩下的數(shù)據(jù)交給另一個調(diào)用方去處理。這個過程看起來非常平常,但是隱藏了兩個問題,第一個問題是如果我們能保證第一次處理的時候,每次都是使用一萬條還好說,如果我們使用的條數(shù)是一個動態(tài)的值呢?顯然,我們需要一個變量來記錄我們究竟用了多少條數(shù)據(jù),和這批數(shù)據(jù)的狀態(tài)。其次,如果這個數(shù)據(jù)量很大會存在一個數(shù)據(jù)傳輸?shù)膯栴}。我們每次都要將一大批數(shù)據(jù)傳來傳去,顯然會消耗很多資源。

還有一個場景是如果我們開發(fā)的是一個比較復(fù)雜的數(shù)據(jù)結(jié)構(gòu),比如一棵多叉樹,下游想要遍歷它的時候,必須要了解它的實現(xiàn)原理才行。這顯然也不太友好。

迭代器的出現(xiàn)正是針對以上這些問題,它的含義也很簡單,有點像是我們遍歷鏈表的時候用到的cur的指針。永遠(yuǎn)指向當(dāng)前的位置,永遠(yuǎn)知道下一個位置在哪里。

容器迭代器

我們先從簡單的元素迭代器開始了解它的用途,我們都知道Python當(dāng)中經(jīng)典的幾個容器:list, tuple和dict。它們都是一個可迭代對象,我們可以直接使用關(guān)鍵字iter獲取一個對應(yīng)的迭代器。

我們來看一個例子:

arr = [1, 3, 4, 5, 9]it = iter(arr)print(next(it))print(next(it))

這是一個非常經(jīng)典的例子,我們首先定義了一個數(shù)組,然后通過iter關(guān)鍵字獲取了一個讀取它的迭代器。有了迭代器之后我們可以通過next關(guān)鍵字獲取迭代器當(dāng)中的下一個元素,我們一共調(diào)用了兩次next,第一次輸出的結(jié)果是1,第二次的結(jié)果是3。和我們剛才說的一樣,我們每一次調(diào)用,它會自動往后移動一格,獲取后面一位的數(shù)據(jù)。

這里有一點需要注意,因為我們創(chuàng)建的數(shù)組當(dāng)中一共只有5個元素,如果我們調(diào)用it的次數(shù)超過5次,那么會引發(fā)超界,Python的解釋器會拋出StopIterat****ion的error。

除了使用next,我們也可以使用for循環(huán)來迭代它:

for i in it: print(i)

這種用法就和我們用for循環(huán)遍歷元素是一樣的。

自定義迭代器

官方的迭代器的用法就這么多,這也不是它的主要用法,它最主要的用法是我們自己創(chuàng)建迭代器。和之前介紹Python自定義排序的時候的思路一樣,我們?yōu)轭愄砑由蟔_iter__方法和__next__方法即可。

其中__iter__方法用來初始化并返回迭代器,關(guān)于它的解釋比較復(fù)雜。在Python當(dāng)中迭代有兩個概念一個是iterable,一個是iterator。協(xié)議規(guī)定iteratble的__iter__方法會返回一個iterator。而iterator本身也是一個iterable對象,自然也需要實現(xiàn)__iter__方法。

我知道這么說可能聽不太明白,我舉個例子,比如說員工和老板,員工沒有審批權(quán)限,只能轉(zhuǎn)達(dá)給老板。我們把員工比喻成iterable對象,老板比喻成iterator。

員工面臨一個問題的時候沒有權(quán)限處理,只能找來老板決定。也就是最終決定的是老板,但如果是老板自己發(fā)現(xiàn)的問題,他完全可以自己就解決了,不需要再去找其他人。所以說我們用iter調(diào)用iterable對象的__iter__的時候,會得到一個iterator,也就是調(diào)用員工返回老板,然后通過調(diào)用iterator的__next__來進(jìn)行迭代。

到這里也就清楚了,只有iterator有__next__方法,而iterable沒有,并且__iter__返回的是一個iterator。然而我們定義的已經(jīng)是iterator了,它同時也是一個iterable對象,所以調(diào)用__iter__時只需要返回self就好了。__next__方法很簡單,對應(yīng)迭代器的next方法,用來返回下一個迭代的元素。

我們來看一個例子:

class PowTwo: '''Class to implement an iterator of powers of two''' def __init__(self, max = 0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n <= self.max: result = 2 ** self.n self.n += 1 return result else: raise StopIteration

這是一個簡單的生成2的冪的迭代器,我們在__iter__里為self.n初始化為0,然后返回自身。在__next__里判斷有沒有迭代結(jié)束,如果結(jié)束的話拋出一個異常。

我們來看使用它的例子:

>>> a = PowTwo(4)>>> i = iter(a)>>> next(i)1>>> next(i)2>>> next(i)4>>> next(i)8>>> next(i)16>>> next(i)Traceback (most recent call last):...StopIteration

我們也可以用for循環(huán)來迭代它:

>>> for i in PowTwo(5):... print(i)... 12481632

迭代器除了可以迭代一個容器或者是像上面這樣自定義迭代方法之外,還可以用來迭代生成器。下面就讓我們一起來看下生成器的概念。

生成器

生成器的概念和迭代器相輔相成,迭代器是生成一個遍歷數(shù)據(jù)的迭代工具,而生成器則是數(shù)據(jù)生成工具。

舉個很簡單的例子,比如說斐波那契數(shù)列我們都知道,從第三個數(shù)開始等于前面兩個數(shù)的和。比如我們想獲取100萬個斐波那契數(shù)列,按照傳統(tǒng)的方法我們需要開辟一個長度是一百萬的數(shù)組,然后按照斐波那契數(shù)列的定義一個一個地計算。顯然這樣會消耗大量的空間,有沒有辦法我們和迭代器那樣構(gòu)建一個生成數(shù)據(jù)的方法,我們每次調(diào)用獲取下一個結(jié)果呢?這樣我們要多少數(shù)據(jù)就調(diào)用多少次就可以了,從根本上解決了存儲的問題。

下面我們來看怎么定義一個生成器。

括號創(chuàng)建法

最簡單的方法真的很簡單,和我們創(chuàng)建list基本上一模一樣。

在Python當(dāng)中,我們經(jīng)常這樣初始化一個數(shù)組:

arr = [i * 3for i in range(10)]

也就是說我們把循環(huán)放在list的定義當(dāng)中,這樣Python會自動執(zhí)行里面的循環(huán),然后將所有循環(huán)的結(jié)果進(jìn)行二次計算后寫入到list當(dāng)中去。我們稍微變形一下,就得到了一個最簡單的生成器。

g = (i * 3for i in range(10))print(next(g))

看清楚了嗎,其實和list沒什么差別,只是我們將最外層的括號從[]換成了()。

這種方法大家應(yīng)該都能看懂,但是可能會有一個疑惑。我們這樣做的意義是什么呢?這樣和上面用[]定義有什么區(qū)別呢?

其實是有區(qū)別的,如果沒有區(qū)別,那么我們用生成器也就沒有意義了。它的區(qū)別也就是生成器的意義,簡單來說,我們前文中已經(jīng)說過了當(dāng)定義一個list的時候,Python會自動將for循環(huán)執(zhí)行一遍,然后將結(jié)果寫入進(jìn)list當(dāng)中。但是生成器不會,雖然我們也用到了for循環(huán),但是它只是起到了限制個數(shù)的作用,在執(zhí)行完這一步之后,Python并不會將for循環(huán)執(zhí)行結(jié)束。只有我們每次調(diào)用next,才會觸發(fā)它進(jìn)行一次循環(huán)。

不相信的同學(xué)可以試試,看看運(yùn)行一下下面兩個語句的區(qū)別:

g = (i for i in range(1000000000))g = [i for i in range(1000000000)]

如果奇怪的事情發(fā)生了,不妨再回到文章來思考一下。

函數(shù)創(chuàng)建法

上面介紹的方法雖然簡單,但是不太實用,因為很多時候我們想要的數(shù)據(jù)構(gòu)造方法會比較復(fù)雜,很難用這種形式展現(xiàn)出來。

所以Python當(dāng)中還為我們提供了一種構(gòu)造生成器的方法,相比起來要稍微復(fù)雜一點點,但是也很好用。我們來看一個例子:

def gtr(n): for i in range(n): yield i

從代碼上來看,我們好像定義了一個函數(shù),某種程度上可以這么理解,但是它返回的結(jié)果并不是一個值,而是一個生成器[2]。

如果你真的去試了,你會得到一個generator類型的實例,這也是Python自帶的生成器的實例。

再仔細(xì)觀察一下,你會發(fā)現(xiàn)這個函數(shù)當(dāng)中的關(guān)鍵字和一般的不太一樣,它沒有使用return,而是使用了yield。yield和return在很大程度上很接近,但是又有些不同。

相同點是當(dāng)我們執(zhí)行到y(tǒng)ield時,和return一樣會將yield之后的內(nèi)容返回給調(diào)用方。比如上面代碼當(dāng)中寫到y(tǒng)ield i,那么我們運(yùn)行next的時候就會獲取到這個i。

不同的地方是,當(dāng)我們下一次再次執(zhí)行的時候,會繼續(xù)從上次yield處開始往下執(zhí)行。有些類似于遞歸的時候,底層的遞歸執(zhí)行結(jié)束回到上層的情況。因此如果我們要獲取多個值,需要在生成器當(dāng)中使用循環(huán)。舉個例子:

def test(): n = 0 whileTrue: if n < 3: yield n n += 1 else: yield10 if __name__ == ’__main__’: t = test() for i in range(10): print(next(t))

我們?nèi)绻麍?zhí)行上面這段代碼,前三個數(shù)是0,1和2,從第四個數(shù)開始一直是10。如果你能看懂這個例子,一定能明白yield的含義。

yield from

接下來要介紹的yield from和yield用法差不多,也是從生成器返回一個結(jié)果,并且下次執(zhí)行的時候從返回的位置開始繼續(xù)執(zhí)行。

但是它有一點和yield不同,我們來看一個經(jīng)典的例子。

def g1(): yield range(5)def g2(): yieldfrom range(5)it1 = g1()it2 = g2()for x in it1: print(x)for x in it2: print(x)

這兩者打印出來的結(jié)果是一樣的,但是邏輯完全不同。在第一個生成器g1當(dāng)中,直接通過yield返回了一個迭代器。也就是說我們for循環(huán)執(zhí)行的其實是range(5),而第二個生成器g2則通過yield from獲取了range(5)這個迭代器當(dāng)中的值進(jìn)行的返回。

也就是說yield from可以返回一個迭代器或者是生成器執(zhí)行next之后的結(jié)果。

最后,我們來看一個yield from使用的一個經(jīng)典場景:二叉樹的遍歷:

class Node: def __init__(self, key): self.key = key self.lchild = None self.rchild = None self.iterated = False self.father = None def iterate(self): if self.lchild isnotNone: yieldfrom self.lchild.iterate() yield self.key if self.rchild isnotNone: yieldfrom self.rchild.iterate()

在這個代碼當(dāng)中我們定義了二叉樹當(dāng)中的一個節(jié)點,以及它對應(yīng)的迭代方法。由于我們用到了yield來返回結(jié)果,所以iterate方法本質(zhì)是一個生成器。再來看iterate方法內(nèi)部,我們通過yield from調(diào)用了iterate,所以我們在執(zhí)行的時候,它會自動繼續(xù)解析node.lchild的iterate,也就是說我們通過yield from實現(xiàn)了遞歸。

當(dāng)我們建好樹之后,可以直接使用root.iterate來遍歷整棵樹。

class Tree: def __init__(self): #建樹過程 self.root = Node(4) self.root.lchild = Node(3) self.root.lchild.father = self.root self.root.rchild = Node(5) self.root.rchild.father = self.root self.root.lchild.lchild = Node(1) self.root.lchild.lchild.father = self.root.lchild self.root.rchild.rchild = Node(7) self.root.rchild.rchild.father = self.root.rchild def iterate(self): yieldfrom self.root.iterate()

通過yield from,我們可以很輕松地利用遞歸的思路來實現(xiàn)樹上的生成器。從而可以很方便地以生成器的思路來遍歷樹上所有的元素。

到這里,關(guān)于Python當(dāng)中迭代器和生成器的知識就算是講完了,這兩者的概念有些接近,但是又不完全一樣,很多初學(xué)者容易搞混淆。

其實可以這么理解,迭代器和生成器遍歷元素的方式是一樣的,都是通過調(diào)用next來獲取下一個元素。我們通過yield創(chuàng)建函數(shù),返回的結(jié)果其實就是生成器生成的數(shù)據(jù)的迭代器。也就是說迭代器只是迭代和獲取數(shù)據(jù)的,但是并不能無中生有地創(chuàng)造數(shù)據(jù)。而生成器的主要作用是創(chuàng)造數(shù)據(jù),它生成出來的數(shù)據(jù)是以迭代器的形式返回的。

舉個例子,你開了一個奶茶店,通過奶茶店每個月可以在銀行賬戶里獲得一筆收入。迭代器就是這個賬戶,通過它你可以獲得一筆一筆的收入。而奶茶店則是一個生成器,它產(chǎn)出數(shù)據(jù),但是是以迭代器的形式返回給你的,也就是以銀行賬戶的方式給你收入。我們拿到銀行卡并不知道它里面的錢是怎么賺來的,只能看到錢,也就是說我們并不知道迭代器背后數(shù)據(jù)的邏輯。但是生成器我們是清楚的,因為錢(生產(chǎn)邏輯)是我們親自賺來的。

以上就是五分鐘帶你搞懂python 迭代器與生成器的詳細(xì)內(nèi)容,更多關(guān)于python 迭代器與生成器的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Python 編程
相關(guān)文章:
主站蜘蛛池模板: 聚合氯化铝价格_聚合氯化铝厂家_pac絮凝剂-唐达净水官网 | 知网论文检测系统入口_论文查重免费查重_中国知网论文查询_学术不端检测系统 | 蓄电池在线监测系统|SF6在线监控泄露报警系统-武汉中电通电力设备有限公司 | 深圳激光打标机_激光打标机_激光焊接机_激光切割机_同体激光打标机-深圳市创想激光科技有限公司 深圳快餐店设计-餐饮设计公司-餐饮空间品牌全案设计-深圳市勤蜂装饰工程 | 铝单板_铝窗花_铝单板厂家_氟碳包柱铝单板批发价格-佛山科阳金属 | 别墅图纸超市|别墅设计图纸|农村房屋设计图|农村自建房|别墅设计图纸及效果图大全 | 中医中药治疗血小板减少-石家庄血液病肿瘤门诊部 | 牛皮纸|牛卡纸|进口牛皮纸|食品级牛皮纸|牛皮纸厂家-伽立实业 | 专业广州网站建设,微信小程序开发,一物一码和NFC应用开发、物联网、外贸商城、定制系统和APP开发【致茂网络】 | 大型工业风扇_工业大风扇_大吊扇_厂房车间降温-合昌大风扇 | 哲力实业_专注汽车涂料汽车漆研发生产_汽车漆|修补油漆品牌厂家 长沙一级消防工程公司_智能化弱电_机电安装_亮化工程专业施工承包_湖南公共安全工程有限公司 | 27PR跨境电商导航 | 专注外贸跨境电商| 小青瓦丨古建筑瓦丨青瓦厂家-宜兴市徽派古典建筑材料有限公司 | 南京技嘉环保科技有限公司-杀菌除臭剂|污水|垃圾|厕所|橡胶厂|化工厂|铸造厂除臭剂 | 盐水蒸发器,水洗盐设备,冷凝结晶切片机,转鼓切片机,絮凝剂加药系统-无锡瑞司恩机械有限公司 | CXB船用变压器-JCZ系列制动器-HH101船用铜质开关-上海永上船舶电器厂 | 点焊机-缝焊机-闪光对焊机-电阻焊设备生产厂家-上海骏腾发智能设备有限公司 | 湖北省煤炭供应链综合服务平台| 减速机三参数组合探头|TSM803|壁挂式氧化锆分析仪探头-安徽鹏宸电气有限公司 | 安徽华耐泵阀有限公司-官方网站 安德建奇火花机-阿奇夏米尔慢走丝|高维|发那科-北京杰森柏汇 | 钢木实验台-全钢实验台-化验室通风柜-实验室装修厂家-杭州博扬实验设备 | 精密五金冲压件_深圳五金冲压厂_钣金加工厂_五金模具加工-诚瑞丰科技股份有限公司 | 上海公司注册-代理记账-招投标审计-上海昆仑扇财税咨询有限公司 上海冠顶工业设备有限公司-隧道炉,烘箱,UV固化机,涂装设备,高温炉,工业机器人生产厂家 | ◆大型吹塑加工|吹塑加工|吹塑代加工|吹塑加工厂|吹塑设备|滚塑加工|滚塑代加工-莱力奇塑业有限公司 | 工控机-工业平板电脑-研华工控机-研越无风扇嵌入式box工控机 | 桂林腻子粉_内墙外墙抗裂砂浆腻子粉推荐广西鑫达涂料厂家供应 | 昆山PCB加工_SMT贴片_PCB抄板_线路板焊接加工-昆山腾宸电子科技有限公司 | 单电机制砂机,BHS制砂机,制沙机设备,制砂机价格-正升制砂机厂家 单级/双级旋片式真空泵厂家,2xz旋片真空泵-浙江台州求精真空泵有限公司 | 安规_综合测试仪,电器安全性能综合测试仪,低压母线槽安规综合测试仪-青岛合众电子有限公司 | 广州二手电缆线回收,旧电缆回收,广州铜线回收-广东益福电缆线回收公司 | GEDORE扭力螺丝刀-GORDON防静电刷-CHEMTRONICS吸锡线-上海卓君电子有限公司 | 除尘布袋_液体过滤袋_针刺毡滤料-杭州辉龙过滤技术有限公司 | 澳门精准正版免费大全,2025新澳门全年免费,新澳天天开奖免费资料大全最新,新澳2025今晚开奖资料,新澳马今天最快最新图库 | 针焰试验仪,灼热丝试验仪,漏电起痕试验仪,水平垂直燃烧试验仪 - 苏州亚诺天下仪器有限公司 | 电位器_轻触开关_USB连接器_广东精密龙电子科技有限公司 | 河南中整光饰机械有限公司-抛光机,去毛刺抛光机,精密镜面抛光机,全自动抛光机械设备 | 东莞市超赞电子科技有限公司 全系列直插/贴片铝电解电容,电解电容,电容器 | 交流伺服电机|直流伺服|伺服驱动器|伺服电机-深圳市华科星电气有限公司 | 创客匠人-让IP变现不走弯路 | 伸缩器_伸缩接头_传力接头-巩义市润达管道设备制造有限公司 | 缠膜机|缠绕包装机|无纺布包装机-济南达伦特机械设备有限公司 |