Python的列表推倒式、生成器及迭代器
Python的列表推倒式、生成器及迭代器
文章目錄
- Python的列表推倒式、生成器及迭代器
- 一、列表推倒式
- **`1.列表推導(dǎo)式`**
- **`2.字典推導(dǎo)式`**
- **`3.集合推導(dǎo)式`**
- 二、生成器
- 生成器的創(chuàng)建方式
- 類似于列表生成式創(chuàng)建
- **`函數(shù)方式創(chuàng)建`**
- **`生成器常用方法`**
- send方法使用舉例
- **`生產(chǎn)者與消費(fèi)者問題`**
- **`日志記錄`**
- **`計(jì)算移動平均值`**
- **`帶裝飾器的計(jì)算移動平均值`**
- **`yield from`**
- **`處理異常`**
- 三、迭代器
- **`可迭代對象與迭代器`**
- 可迭代和迭代器區(qū)別
- 1. 區(qū)別:
- 2.迭代器:
- 3. **迭代器的優(yōu)點(diǎn)**:
- 4.相同點(diǎn)與不同點(diǎn):
- 5.判斷迭代器和可迭代的方法
- **`判斷range函數(shù)和map函數(shù)`**
- **`深入了解Iterator對象`**
- **`迭代器小結(jié)`**
一、列表推倒式
從母雞下蛋的故事講起
1 老母雞 = ('雞蛋%s'%i for i in range(10)) 2 print(老母雞) 3 for 蛋 in 老母雞: 4 print(蛋) 5 g = (i*i for i in range(10)) 6 for i in g: 7 print(i)- ①把列表解析的[]換成()得到的就是生成器表達(dá)式
- ②列表解析與生成器表達(dá)式都是一種便利的編程方式,只不過生成器表達(dá)式更加節(jié)省內(nèi)存空間
- ③Python不但使用迭代器協(xié)議,讓for循環(huán)變得更加通用。大部分內(nèi)置函數(shù),也是使用迭代器協(xié)議訪問對象的。
例如sum函數(shù)是python的內(nèi)置函數(shù),該函數(shù)使用迭代器協(xié)議訪問對象,而生成器實(shí)現(xiàn)了迭代器協(xié)議。
1 sum(i*2 for i in range(4))1.列表推導(dǎo)式
例一:30以內(nèi)所有能被3整除的數(shù) 1 # 列推導(dǎo)式 2 # [滿足條件的元素相關(guān)的操作 for 元素 in 可迭代數(shù)據(jù)類型 if 元素相關(guān)的條件] 3 # 30以為所有能被3整除的數(shù)字 4 print([i for i in range(30) if i%3 == 0]) # 完整的列表推導(dǎo)式 例二:30以內(nèi)所有能被3整除的數(shù)的平方1 # 30以為所有能被3整除的平方 2 print([i*i for i in range(30) if i%3 == 0]) # 完整的列表推導(dǎo)式 例三:找到嵌套列表中名字包含‘e’的所有名字1 # 找到嵌套列表中名字中含有'e'的所有名字 2 names = [['Tom','Billy','Jefferson','Andrew','Wesley','Steven','Joe'], 3 ['Alice','Jill','Ana','Wendy','Jennifer','Sherry','Eva']] 4 ret = [name for lst in names for name in lst if name.count('e') == 2] 5 print(ret)2.字典推導(dǎo)式
例一:將一個(gè)字典的key和value對調(diào)1 # 將一個(gè)字典的key和value對調(diào) 2 mcase = {'a':10,'b':34} 3 mcase_frequency = {mcase[k]:k for k in mcase} 4 print(mcase_frequency)例二:合并大小寫對應(yīng)的value值,講key統(tǒng)一成大寫1 # 合并大小寫對應(yīng)的value值,將key統(tǒng)一成小寫 2 # {'a':10+7,'b':34,'z':3} 3 mcase = {'a':10,'b':34,'A':7,'Z':3} 4 mcase_frequency = {k.lower():mcase.get(k.lower(),0)+mcase.get(k.upper(),0) for k in mcase}3.集合推導(dǎo)式
例子:計(jì)算列表中每個(gè)值的平方,自帶去重復(fù)的功能1 # 集合推導(dǎo)式 自帶去重功能 2 squared = {x**2 for x in [1,-1,2]} 3 print(squared)二、生成器
- 通過列表生成式,我們可以直接創(chuàng)建一個(gè)列表。但是,受到內(nèi)存限制,列表容量肯定是有限的。而且,創(chuàng)建一個(gè)包含100萬個(gè)元素的列表,不僅占用很大的存儲空間,
- 如果我們僅僅需要訪問前面幾個(gè)元素,那后面絕大多數(shù)元素占用的空間都白白浪費(fèi)了。
- 所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環(huán)的過程中不斷推算出后續(xù)的元素呢?
- 這樣就不必創(chuàng)建完整的list,從而節(jié)省大量的空間。在Python中,這種一邊循環(huán)一邊計(jì)算的機(jī)制,稱為生成器:generator。
- 要?jiǎng)?chuàng)建一個(gè)generator,有很多種方法。第一種方法很簡單,只要把一個(gè)列表生成式的[]改成(),就創(chuàng)建了一個(gè)generator
生成器的創(chuàng)建方式
函數(shù)有了yield之后
類似于列表生成式創(chuàng)建
L = [x * x for x in range(10)] gen_L = (x * x for x in range(10)) # 生成器存放計(jì)算公式print(L) print(gen_L) print(next(gen_L)) # 取值 print(next(gen_L))""" [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] <generator object <genexpr> at 0x0000000001DFDF10> generator 就是生成器的意思 0 1 """- generator保存的是算法,每次調(diào)用next(g)就計(jì)算出g的下一個(gè)元素的值,直到計(jì)算到最后一個(gè)元素,沒有更多的元素時(shí),拋出StopIteration的錯(cuò)誤,不調(diào)next函數(shù)并不會生層元素,就會節(jié)省內(nèi)存,使用for循環(huán)可以解決異常
- generator非常強(qiáng)大。如果推算的算法比較復(fù)雜,用類似列表生成式的for循環(huán)無法實(shí)現(xiàn)的時(shí)候,還可以用函數(shù)來實(shí)現(xiàn)。
注意a, b = b, a + b 相當(dāng)于
a, b = 0, 1 a, b = b, a + b print(a, b) a, b = 0, 1 t = a a = b b = t + b print(a, b)""" 1 1 1 1 """函數(shù)方式創(chuàng)建
def range2(n):count = 0while count < n :print(count)stop_flag = yield count #中斷并返回return countif stop_flag == 'stop':print('stop iteration...')breakcount += 1b = range2(5) # 得到生成器 next(b) next(b) """ 0 Traceback (most recent call last):File "C:/Users/jingjing/PycharmProjects/py3Project", line 53, in <module>next(b) StopIteration: 0 """生成器常用方法
next() # 喚醒生成器并繼續(xù)執(zhí)行 send("stop") """ 1. 喚醒并繼續(xù)執(zhí)行 2. 發(fā)送一個(gè)信息到生成器內(nèi)部 注意生成器在剛開始,函數(shù)沒有執(zhí)行到y(tǒng)ield成為掛起狀態(tài)時(shí),不能調(diào)用send("stop") 只能send('None')相當(dāng)于next()使函數(shù)執(zhí)行到y(tǒng)ield """send方法使用舉例
send方法第一次調(diào)用必須傳遞一個(gè)None參數(shù),后續(xù)才可以傳遞自定義參數(shù)
def range2(n):count = 0while count < n:print('count', count)count += 1sign = yield count # returnif sign == 'stop':print("---sign", sign)breakprint('sin...', sign)return 3333new_range = range2(3)next(new_range) new_range.send(None) new_range.send("stop") """ count 0 sin... None count 1 ---sign stop Traceback (most recent call last):File "C:/Users/jingjing/PycharmProjects/py3Project.py", line 22, in <module>new_range.send("stop") StopIteration: 3333 """生產(chǎn)者與消費(fèi)者問題
import timedef consume(name):print("%s 準(zhǔn)備吃包子啦!" % name)while True:y = yieldprint("包子[%s]來了,被[%s]吃了!" % (y, name))def producer(name):c = consume(name)next(c) # 函數(shù)執(zhí)行到y(tǒng)ieldfor i in range(1, 3):time.sleep(1)print("做了{(lán)}個(gè)包子!".format(i))c.send(i) # 把i傳到y(tǒng)ieldproducer('qian')""" qian 準(zhǔn)備吃包子啦! 做了1個(gè)包子! 包子[1]來了,被[qian]吃了! 做了2個(gè)包子! 包子[2]來了,被[qian]吃了! """日志記錄
def logger(filename):"""日志方法:param filename: log filename :param channel: 輸出的目的地,屏幕(terminal),文件(file),屏幕+文件(both):return: """print('start logger')while True:msg = yieldprint("msg", msg)l = logger('USER.TXT') l.__next__()l.send('hi') l.send('hi,file')計(jì)算移動平均值
# 必須先用next再用send def average():total=0 #總數(shù)day=0 #天數(shù)average=0 #平均數(shù)while True:day_num = yield average #average=0print('average', average)total += day_numday += 1average = total/day avg=average() #直接返回生成器 next(avg)#激活生成器,avg.send(None),什么都不傳的時(shí)候send和next的效果一樣 print(avg.send(10)) print(avg.send(20))#send 1.傳值 2.next print(avg.send(30)) """ average 0 10.0 average 10.0 15.0 average 15.0 20.0 """帶裝飾器的計(jì)算移動平均值
# 讓裝飾器去激活 def wrapper(func):def inner(*args, **kwargs):print('execute wrapper')a = func(*args, **kwargs)next(a)return areturn inner@wrapper def average():total=0 #總數(shù)day=0 #天數(shù)average=0 #平均數(shù)while True:day_num = yield average #average=0print('average', average)total += day_numday += 1average = total/dayavg = average() print(avg.send(10)) print(avg.send(20))#send 1.傳值 2.next print(avg.send(30)) """ execute wrapper average 0 10.0 average 10.0 15.0 average 15.0 20.0 """yield from
def gen1():for c in 'AB':yield cfor i in range(3):yield i print(gen1()) print(list(gen1()))def gen2():yield from 'AB' # 相當(dāng)于 for c in 'AB': yield cyield from range(3) print(gen2()) print(list(gen2()))處理異常
def fib(max):n, a, b = 0, 0, 1while n < max:print('before yield')yield b # 把函數(shù)的執(zhí)行過程凍結(jié)在這一步,并且把b的值 返回給外面的next()a, b = b, a + bn = n + 1return 'done'g = fib(6) while True:try:x = next(g)print('g:', x)except StopIteration as e:print('Generator return value:', e.value)break三、迭代器
可迭代對象與迭代器
迭代:可以將某個(gè)數(shù)據(jù)集內(nèi)的數(shù)據(jù)“一個(gè)挨著一個(gè)的取出來”,就叫做迭代。就像for循環(huán)一樣取值。
可迭代協(xié)議:可以被迭代要滿足要求的就叫做可迭代協(xié)議。內(nèi)部實(shí)現(xiàn)了__iter__方法
iterable:可迭代的------對應(yīng)的標(biāo)志
字符串、列表、元組、字典、集合都可以被for循環(huán),說明他們都是可迭代的。
可以使用isinstance()判斷一個(gè)對象是否是Iterable對象:
可以被next函數(shù)調(diào)用并且返回下一個(gè)值的對象叫迭代器
可迭代和迭代器區(qū)別
1. 區(qū)別:
- 可迭代的結(jié)構(gòu)并不代表一定是一個(gè)迭代器,如:列表是可迭代的但不是迭代器;生層式是可迭代的也是迭代器
- 可以通過系統(tǒng)函數(shù)iter()將一個(gè)可迭代的結(jié)構(gòu)變成一個(gè)迭代器
2.迭代器:
生成器不但可以作用于for循環(huán),還可以被next()函數(shù)不斷調(diào)用并返回下一個(gè)值,直到最后拋出StopIteration錯(cuò)誤表示無法繼續(xù)返回下一個(gè)值了
- 迭代器協(xié)議:內(nèi)部實(shí)現(xiàn)了__iter__,__next__方法。
3. 迭代器的優(yōu)點(diǎn):
如果用了迭代器,節(jié)約內(nèi)存,方便操作,生成器是一種特殊的迭代器(Iterator)。
4.相同點(diǎn)與不同點(diǎn):
- 可迭代和迭代器的相同點(diǎn):都可以用for循環(huán)
- 可迭代和迭代器的不同點(diǎn):就是迭代器內(nèi)部多實(shí)現(xiàn)了一個(gè)__next__方法
5.判斷迭代器和可迭代的方法
- 第一種:判斷內(nèi)部是不是實(shí)現(xiàn)了__next__方法
- print(‘next’ in dir(range(12))) #查看’next’是不是在range()方法執(zhí)行之后內(nèi)部是否有__next__
- print(‘iter’ in dir(range(12))) #查看’next’是不是在range()方法執(zhí)行之后內(nèi)部是否有__next__
- 第二種:
- Iterable 判斷是不是可迭代對象
- Iterator 判斷是不是迭代器
判斷range函數(shù)和map函數(shù)
深入了解Iterator對象
生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數(shù):
你可能會問,為什么list、dict、str等數(shù)據(jù)類型不是Iterator?
- 這是因?yàn)镻ython的Iterator對象表示的是一個(gè)數(shù)據(jù)流,Iterator對象可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)數(shù)據(jù),
- 直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration錯(cuò)誤??梢园堰@個(gè)數(shù)據(jù)流看做是一個(gè)有序序列,但我們卻不能提前知道序列的長度,
- 只能不斷通過next()函數(shù)實(shí)現(xiàn)按需計(jì)算下一個(gè)數(shù)據(jù),所以Iterator的計(jì)算是惰性的,只有在需要返回下一個(gè)數(shù)據(jù)時(shí)它才會計(jì)算。
- Iterator甚至可以表示一個(gè)無限大的數(shù)據(jù)流,例如全體自然數(shù)。而使用list是永遠(yuǎn)不可能存儲全體自然數(shù)的。
迭代器小結(jié)
- 凡是可作用于for循環(huán)的對象都是Iterable類型;
- 凡是可作用于next()函數(shù)的對象都是Iterator類型,它們表示一個(gè)惰性計(jì)算的序列;
- 集合數(shù)據(jù)類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數(shù)獲得一個(gè)Iterator對象。
- Python3的for循環(huán)本質(zhì)上就是通過不斷調(diào)用next()函數(shù)實(shí)現(xiàn)的,例如:
總結(jié)
以上是生活随笔為你收集整理的Python的列表推倒式、生成器及迭代器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小Q购物
- 下一篇: Python中的匿名函数和函数式编程