Python迭代器、生成器與裝飾器(筆記補(bǔ)充)
〇、前言
筆記補(bǔ)充系列補(bǔ)全原視頻筆記沒有出現(xiàn)的Python知識(shí),所以內(nèi)容非常依賴參考內(nèi)容,專欄有錯(cuò)誤的地方歡迎指出
WPS筆記及其他內(nèi)容百度網(wǎng)盤鏈接:
https://pan.baidu.com/s/1fTwjyoM81_OOAccPNhGE9Q?pwd=h3fg
參考網(wǎng)址在下面列出:
https://docs.python.org/zh-cn/3.11/reference/expressions.html?
https://www.runoob.com/python3/python3-iterator-generator.html
https://www.runoob.com/w3cnote/python-func-decorators.htm
(1)、迭代器
1.?概念
迭代是訪問集合元素的一種方式,迭代器是一個(gè)可以記住遍歷的位置的對(duì)象
2.?過程
迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會(huì)后退
3.?可被創(chuàng)建迭代器對(duì)象
字符串、列表、元組等可迭代對(duì)象
4.?基本方法
4.1.?iter()
生成迭代器對(duì)象
iter(object[, sentinel)
object——可迭代對(duì)象
sentinel——如果該參數(shù)被傳遞,則參數(shù)object必須是一個(gè)可調(diào)用對(duì)象,此時(shí),iter創(chuàng)建了一個(gè)迭代器對(duì)象,每次調(diào)用該對(duì)象__next__()方法時(shí),都會(huì)調(diào)用object(不帶參數(shù)),直到返回值等于sentinel,這時(shí)調(diào)用__next__()方法會(huì)引發(fā)StopIteration
補(bǔ)充:內(nèi)置函數(shù)callable()
檢查一個(gè)對(duì)象是否是可調(diào)用的。如果返回True,對(duì)象仍然可能調(diào)用失?。坏绻祷谾alse,調(diào)用對(duì)象object絕對(duì)不會(huì)成功
# 例如上面iter()自動(dòng)調(diào)用object,如果需要參數(shù),會(huì)調(diào)用失敗
callable(object)
object——對(duì)象
大概有以下7類可調(diào)用對(duì)象: ?# 可調(diào)用對(duì)象可用"對(duì)象名()"進(jìn)行調(diào)用
1.用戶自定義的函數(shù)
2.內(nèi)置函數(shù)
3.內(nèi)置方法
4.類方法
5.類
6.類定義__call__方法的類的實(shí)例
7.生成器函數(shù)
4.2.?next()
返回迭代器的下一項(xiàng)
next(iterable[, default])
iterable——迭代器對(duì)象
default——可選,用于設(shè)置在沒有下一個(gè)元素時(shí)返回該默認(rèn)值,不設(shè)置且沒有下一個(gè)元素時(shí)觸發(fā)StopIteration異常
4.3.?for語句
迭代器對(duì)象可以使用常規(guī)for語句進(jìn)行遍歷
示例:
運(yùn)行結(jié)果:
True True
1
2
3
stop
[隨機(jī)數(shù)量的2 + None]
5.?創(chuàng)建一個(gè)迭代器
把一個(gè)類作為一個(gè)迭代器使用需要在類中實(shí)現(xiàn)兩個(gè)方法
5.1.?__iter__()方法
返回一個(gè)特殊的迭代器對(duì)象,這個(gè)迭代器對(duì)象實(shí)現(xiàn)了__next__()方法并通過StopIteration標(biāo)識(shí)迭代的完成
5.2.?__next__()方法
返回迭代器對(duì)象的下一項(xiàng)
示例:
運(yùn)行結(jié)果:
3 6 12 24 48 96 192 384
6.?StopIteration異常
用于標(biāo)識(shí)迭代的完成,防止出現(xiàn)無限循環(huán)的情況,在__next__()方法中可設(shè)置完成指定循環(huán)次數(shù)后觸發(fā)該異常來結(jié)束迭代(raise語句)
示例:
運(yùn)行結(jié)果:
3 6 12 24 48 96 192 384 768 1536 stop
(2)、生成器
1.?概念
在迭代的同時(shí)生成元素,本質(zhì)上也是迭代器,能節(jié)省內(nèi)存
2.?創(chuàng)建
2.1.?使用生成式
(表達(dá)式 for 自定義變量 in 可迭代對(duì)象)
2.2.?使用生成器函數(shù)
3.?生成器函數(shù)
3.1.?定義
函數(shù)內(nèi)使用了yield語句的函數(shù)
3.2.?作用
調(diào)用函數(shù)時(shí),不會(huì)執(zhí)行函數(shù)中的代碼,返回值是一個(gè)生成器(對(duì)象)
4.?調(diào)用
使用for循環(huán)或next(),與迭代器相同
5.?生成器函數(shù)創(chuàng)建的生成器被調(diào)用時(shí)
執(zhí)行生成器函數(shù)中的代碼,每次遇到y(tǒng)ield語句時(shí)會(huì)暫停并保存當(dāng)前所有的運(yùn)行信息,返回yield的值,并在下一次被調(diào)用時(shí)從當(dāng)前位置繼續(xù)執(zhí)行(yield語句下一行)
6.?yield語句
yield 值[, 值] ?# 后面加多個(gè)數(shù)值,返回的值是元組類型
示例:
運(yùn)行結(jié)果:
<class 'generator'>
2 4 6 8 10 12 14 16 18 20 stop
<class 'generator'>
3 6 12 24 48 96 192 384 768 1536
7.?生成器對(duì)象的方法
7.1.?__next__()
相當(dāng)于next(生成器對(duì)象)或使用for循環(huán),這會(huì)啟動(dòng)生成器,或是從上次執(zhí)行到y(tǒng)ield語句的位置恢復(fù)執(zhí)行,返回下一次執(zhí)行到y(tǒng)ield語句的值
7.2.?send(value)
恢復(fù)執(zhí)行生成器,并在上次執(zhí)行到y(tǒng)ield語句此返回值value,也會(huì)返回下一次執(zhí)行到y(tǒng)ield語句的值
# 如果想用send()啟動(dòng)生成器,則必須先傳入None,因?yàn)榇藭r(shí)函數(shù)沒有接收值value的yield語句
7.3.?throw(value)/throw(type[, value[, traceback]])
在生成器暫停處引發(fā)異常,并返回下一個(gè)值(如果生成器沒有生成則引發(fā)StopIteration異常)
# 生成器沒有捕獲該異?;蚴且l(fā)另一個(gè)異常則將異常傳遞給調(diào)用處
7.4.?close()
在生成器暫停處引發(fā)GeneratorExit
如果生成器函數(shù)正常退出、關(guān)閉或引發(fā)GeneratorExit(由于未捕獲該異常)則關(guān)閉并停止阻塞調(diào)用處
如果生成器產(chǎn)生一個(gè)值,則關(guān)閉并引發(fā)RuntimeError
如果生成器引發(fā)其他異常,則將異常傳遞給調(diào)用處
如果生成器已經(jīng)由于異常關(guān)閉或正常退出則close()不會(huì)任何事
# GeneratorExit繼承自BaseException而不是Exception
# 如果生成器在被銷毀前沒有恢復(fù)執(zhí)行,則將調(diào)用它的close()方法
示例:
運(yùn)行結(jié)果:
a
b
ValueError異常被捕獲
生成器已關(guān)閉
8.?yield from語句
8.1.?概念
委托給子迭代器
8.2.?語法
yield from <expr>
<expr>必須是一個(gè)可迭代對(duì)象(可以是列表、元組、迭代器、生成器等)
8.3.?作用
當(dāng)執(zhí)行到該語句時(shí),生成器會(huì)迭代該可迭代對(duì)象<expr>,并將迭代的值傳遞給調(diào)用處(一次調(diào)用傳遞一個(gè)值)。send()和throw()方法傳入的值或異常會(huì)傳遞給該對(duì)象的相應(yīng)方法,如果沒有則send()引發(fā)AttributeError或TypeError,而throw()引發(fā)傳入的異常
當(dāng)下層迭代器完成時(shí),被引發(fā)的StopIteration實(shí)例的value屬性會(huì)作為該語句的返回值
補(bǔ)充:在生成器中,使用return語句會(huì)結(jié)束迭代,并且return返回的值會(huì)作為引發(fā)StopIteration實(shí)例的value屬性的值
示例:
運(yùn)行結(jié)果:
n的值為: 下層迭代結(jié)束
n的值為: 下層迭代結(jié)束
--------
[1, 2, 3, 101, 102, 103, 201, 202, 203]
(3)、裝飾器(decorator/wrapper)
1.?函數(shù)對(duì)象
Python中一切皆對(duì)象,函數(shù)是一個(gè)對(duì)象,可以賦值給變量
示例:
運(yùn)行結(jié)果:
1
------
1
<function a at [內(nèi)存地址]>
2.?從函數(shù)中返回函數(shù)
函數(shù)對(duì)象可以作為函數(shù)的返回值
示例:
運(yùn)行結(jié)果:
<function a at [內(nèi)存地址]> 1
3.?將函數(shù)作為參數(shù)傳給另一個(gè)函數(shù)
函數(shù)對(duì)象也可以作為函數(shù)的參數(shù)
示例:
運(yùn)行結(jié)果:
1
4.?函數(shù)裝飾器
4.1.?概念
返回值是另一個(gè)函數(shù)(可調(diào)用對(duì)象)的函數(shù),該函數(shù)在不修改被裝飾對(duì)象(函數(shù))源代碼和調(diào)用方式的前提下為被裝飾對(duì)象(函數(shù))添加新功能
4.2.?使用
補(bǔ)充:閉包
閉包是能讀取其他函數(shù)內(nèi)部變量的函數(shù)。語法通常是在函數(shù)A內(nèi)創(chuàng)建函數(shù)B,并且函數(shù)A返回了函數(shù)B,函數(shù)B就是閉包,函數(shù)A的變量和參數(shù)叫自由變量
4.2.1.?簡(jiǎn)單裝飾器
示例:
運(yùn)行結(jié)果:
6 <function plus_1.<locals>.wrapper at [內(nèi)存地址]>
# 函數(shù)five原來作用是輸出五,使用deco修飾后有了輸出值加一的功能
過程分析:
1.定義plus_1裝飾器函數(shù),參數(shù)是函數(shù)對(duì)象func,返回值是嵌套在里面的wrapper函數(shù)
2.將five函數(shù)進(jìn)行裝飾,調(diào)用plus_1函數(shù)并將five函數(shù)對(duì)象傳參,將返回值wrapper函數(shù)對(duì)象賦值給five函數(shù)對(duì)象
3.這時(shí)five的內(nèi)存地址指向wrapper,調(diào)用five就相當(dāng)于調(diào)用wrapper函數(shù),wrapper里面的func就是還未被修飾的five函數(shù)(原始函數(shù)five)
4.2.2.?語法糖寫法
補(bǔ)充:語法糖
語法糖指計(jì)算機(jī)語言中添加的某種語法,這種語法對(duì)語言的功能并沒有影響,但是更方便程序員使用,通常也能夠增加程序的可讀性,從而減少程序代碼出錯(cuò)的機(jī)會(huì)
使用"@裝飾器名"對(duì)函數(shù)進(jìn)行裝飾,放在函數(shù)開始定義的地方
示例:
運(yùn)行結(jié)果:
6 <function plus_1.<locals>.wrapper at [內(nèi)存地址]>
4.3.?多個(gè)裝飾器
一個(gè)函數(shù)可以同時(shí)被多個(gè)裝飾器裝飾,裝飾器的執(zhí)行順序是從里到外、
示例:
運(yùn)行結(jié)果:
11 ?# 先執(zhí)行乘2,再加1
4.4.?被裝飾函數(shù)的參數(shù)
當(dāng)被裝飾函數(shù)需要參數(shù)時(shí),可以在定義wrapper()函數(shù)時(shí)指定參數(shù)*args, **kwargs,再在wrapper()函數(shù)中調(diào)用原始函數(shù)時(shí)將參數(shù)傳遞進(jìn)去。使用*args, **kwargs的目的是確保任何參數(shù)都能適用
示例:
運(yùn)行結(jié)果:
9 ?# (3+5)+1
4.5.?帶參數(shù)的裝飾器
4.5.1.?概念
在調(diào)用裝飾器時(shí)提供其他參數(shù),可根據(jù)不同參數(shù)實(shí)現(xiàn)不同的裝飾功能
4.5.2.?使用
將原有裝飾器再進(jìn)行一次嵌套,嵌套的函數(shù)帶參數(shù)并返回原有裝飾器
# 可以理解為一個(gè)含有參數(shù)的閉包
調(diào)用時(shí)"@裝飾器名(參數(shù)[, ...])" ?# 這里裝飾器名是裝飾器最外層的函數(shù)名
示例:
運(yùn)行結(jié)果:
3 12
4.6.?保留被裝飾函數(shù)的屬性
函數(shù)被裝飾后,原函數(shù)名的元信息變成了裝飾器函數(shù)的信息,如果需要保留原函數(shù)的元信息(也就是將原函數(shù)的元信息,拷貝到相應(yīng)的裝飾器函數(shù)里),就可以使用內(nèi)置的裝飾器@functools.wraps
# 元信息是例如__name__、__doc__等屬性
# functools是Python內(nèi)置模塊,使用前需要載入
在裝飾器wrapper函數(shù)前用"@functools.wraps(func)"裝飾
# func是傳入裝飾器的函數(shù)對(duì)象
示例:
運(yùn)行結(jié)果:
c d
wrapper_a d
5.?類裝飾器
5.1.?概念
類也可以作為裝飾器,類裝飾器的返回值是一個(gè)類的實(shí)例對(duì)象。相比函數(shù)裝飾器,類裝飾器具有靈活度大、高內(nèi)聚、封裝性等優(yōu)點(diǎn)
5.2.?使用
由于裝飾器的返回值是一個(gè)可調(diào)用對(duì)象,所以必須在類里面實(shí)現(xiàn)__call__()方法。__call__()方法在你每調(diào)用一個(gè)類的實(shí)例時(shí)會(huì)被執(zhí)行一次
示例1:
運(yùn)行結(jié)果:
[運(yùn)行時(shí)間]
1000000
示例2:
運(yùn)行結(jié)果:
# 三秒過后
[運(yùn)行時(shí)間]
1000000 wrapper