[BZOJ3514]Codechef MARCH14 GERALD07加強版 題解 [LCT+主席樹]
阿新 • • 發佈:2020-12-22
迭代器和生成器
迭代器:
一、可迭代物件:
1. 能夠通過遍歷取出值的都是可迭代物件,常見的可迭代物件有:str、list、tuple、dict、set、range、filter、map。
2. 案例:
# 01.列表
a = [1,2,3,4,5]
for i in a:
print(i, end=" ")
# 1 2 3 4 5
# 02.元組
b = (1,2,3,4)
for x in b:
print(x, end=" ")
# 1 2 3 4
# 03.字典
c = {"username" :"紅包來了","age":18}
#
# 001.直接輸出items()
print(c.items())
# dict_items([('username', '紅包來了'), ('age', 18)])
#
# 002.將items()拆包
for key,value in c.items():
print(key,value)
# username 紅包來了
# age 18
#
# 003.通過key輸出
for key in c:
print(key,c[key])
# username 紅包來了
# age 18
#
# 004.通過keys()輸出
for x in c.keys():
print(x,c[x])
# username 紅包來了
# age 18
# 04.字串
str01 = "daifaodafa'o"
for i in str01:
print(i, end=" ")
# d a i f a o d a f a ' o
# 05.range()
for x in range(100):
print(x)
# filter 和 map 將在下面詳細的講解
我們分析對可迭代物件進行迭代使用的過程,發現每迭代一次(即在for…in…中每迴圈一次)都會返回物件中的下一條資料,一直向後讀取資料直到迭代了所有資料後結束。那麼,在這個過程中就應該有一個“人”去記錄每次訪問到了第幾條資料,以便每次迭代都可以返回下一條資料。我們把這個能幫助我們進行資料迭代的“人”稱為迭代器(Iterator)
3. 介紹一種判斷是否為可迭代物件的方法,具體參照下列程式碼:
from collections.abc import Iterable
# 匯入collections包abc檔案中的類 Iterable (Iterable 是可迭代物件的意思)
test = "英雄城市武漢"
print(type(test))
# 列印type(test)是什麼型別 <class 'str'>
print(isinstance(test,str)) # True
# isinstance方法是判斷某個物件是不是某種資料型別,如果是就返回True,否則返回False
print(isinstance(test,Iterable)) # True
# 表示字串是一個可迭代物件
# 注意: isinstance的第一個引數 一定是個物件
# 第二個引數 是個資料型別或者Iterable
4. 自定義迭代器:
情景一:
from collections.abc import Iterable
class Demo(object):
def __init__(self,x):
self.x = x
self.count = 0
d =Demo(10)
print(isinstance(d,Iterable)) # 不是可迭代物件 返回False
情景二:
from collections.abc import Iterable
class Demo(object):
def __init__(self,x):
self.x = x
self.count = 0
# 只要重寫了__iter__ 這個方法 例項化物件才是可迭代物件
def __iter__(self):
return self
# 每一次呼叫都會呼叫一次 __next__ 方法 獲取返回值
def __next__(self):
self.count += 1
if self.count <= self.x:
return self.count - 1
else:
raise StopIteration # 迭代器終止
d = Demo(10)
# 01:自定義迭代器的類必須要將__iter__ 和 __next__ 方法重構
print(isinstance(d,Iterable)) # 重寫 __iter__ 方法就是可迭代物件 返回True
# 02:獲取物件的跌打器的方法
test = d.__iter__() # 使用d.__iter__()就可以獲取到物件的迭代器
print(test) # <__main__.Demo object at 0x00000216871A7400>
print(test.__next__()) # 結果:0 這樣只能將迭代器中的第一個元素取出來
# 03:獲取物件的跌打器的方法及輸出1
test = iter(d) # 內建函式 iter() 獲取可迭代物件的迭代器
print(next(test)) # 0
print(next(test)) # 1
print(next(test)) # 2
.....
print(next(test)) # 8
print(next(test)) # 9
# print(next(test)) # 丟擲異常 raise StopIteration # 迭代器終止 StopIteration
# 每輸出一次,都會講迭代器的指標指向下一個元素直到全部取完,如果列印行超出迭代次數就會報錯
# 04:獲取物件的跌打器的方法及輸出2
test = iter(d) # 內建函式 iter() 獲取可迭代物件的迭代器 即得到__iter__的返回值self
count = 0
while True:
print(next(test))
count += 1
if count == 10: # 例項化物件時傳參10
break
# 這裡使用while迴圈可以將結果輸出,不能使用for(會丟擲異常)
# 05:直接輸出結果
for x in Demo(10):
print(x)
# for... in...:迴圈的本質就是呼叫可迭代物件的__iter__方法 獲取這個方法的返回值‘
# 這個返回值 需要是個物件 然後在呼叫這個物件的 __next__ 方法
# 執行結果:0 1 2 3 4 5 6 7 8 9
5. 迭代器案例: 輸出斐波那契數列的前n個數
class Fibonacci(object):
def __init__(self, num):
self.num = num
self.num1 = 1
self.num2 = 1
self.count = 0 # 記錄迭代次數
def __iter__(self):
return self
def __next__(self):
self.count += 1
if self.count <= self.num:
# x 存放最終結果
x = self.num1
self.num1, self.num2 = self.num2, self.num1 + self.num2
return x
else:
raise StopIteration
f = Fibonacci(30)
res = []
for i in f:
res.append(i)
print(res)
print(end1-start1) # 0.0
6. for … in … 迴圈的本質:
for item in Iterable 迴圈的本質就是先通過iter()函式獲取可迭代物件Iterable的迭代器,然後對獲取到的迭代器不斷呼叫next()方法來獲取下一個值並將其賦值給item,當遇到StopIteration的異常後迴圈結束。
生成器:
要建立一個生成器,有很多種方法。下面就一一介紹:
1. 列表生成式 最簡單的生成器
# 01.列表生成式
res = [i for i in range(10)]
print(res)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 這個得到的是一個列表,廣義上也是一個生成器,列表可以迭代
# 02.最簡單的生成器
test = (x for x in range(10))
print(type(test))
# <class 'generator'> 這就是一個生成器,
2. 使用yield來定義一個生成式
def my_generator(n):
i = 0
while i<n:
yield i # 將函式變成了一個生成器 本來我們的函式 用return來表示結束
i += 1
res = my_generator(20)
for x in res:
print(x)
# yield 將值返回給res後,並沒有和return一樣的終止程式的功能,相反,它會繼續迭代,直到滿足迭代終止條件:i>n
3. 生成器案例
def fibonacci(n):
num1 = num2 = 1
count = 0 # 下標
while count < n:
yield num1
num1, num2 = num2, num1 + num2
count += 1
f = fibonacci(10)
# 01:迴圈輸出:
for i in f:
print(i, end=" ")
# 結果:1 1 2 3 5 8 13 21 34 55
# 02.使用next輸出
test = next(iter(f))
print(next(iter(f)))
.....(8個)
print(next(iter(f)))
4.send()函式的使用:
- . 首先需要明確一點,生成器定義在函式內,要使生成器工作,就需要將生成器喚醒,前邊介紹的next()函式就具備這一功能,在這裡,介紹另一種喚醒函式send():
- 使用send()函式的一個好處是可以在喚醒的同時向斷點處傳入一個附加資料。
- 案列:
n [10]: def gen(): ....: i = 0 ....: while i<5: ....: temp = yield i ....: print(temp) ....: i+=1 # 下面是ipython的輸出語句,一句一輸出 # 使用send In [43]: f = gen() In [44]: next(f) # 這裡是使用next來取出迭代的值,不能傳引數。 Out[44]: 0 In [45]: f.send('haha') # 傳了一個字串"haha",輸出了結果 haha Out[45]: 1 In [46]: next(f) None Out[46]: 2 In [47]: f.send('haha') haha Out[47]: 3
總結:
迭代器:
- 自定義的迭代器需要將魔法方法__ next __ 和__ iter __ 重構, __ iter __ 中返回的是迭代器物件,而 __ next __ 中編寫的則是迭代規則。
- 自定義的迭代器是一個類,應該按照類的操作呼叫
生成器:
- 使用了yield關鍵字的函式不再是函式,而是生成器。(使用了yield的函式就是生成器)
- yield關鍵字有兩點作用:
- 儲存當前執行狀態(斷點),然後暫停執行,即將生成器(函式)掛起
- 將yield關鍵字後面表示式的值作為返回值返回,此時可以理解為起到了return的作用
- 可以使用next()函式讓生成器從斷點處繼續執行,即喚醒生成器(函式)
- Python3中的生成器可以使用return返回最終執行的返回值,而Python2中的生成器不允許使用return返回一個返回值(即可以使用return從生成器中退出,但return後不能有任何表示式)。