1. 程式人生 > 實用技巧 >[BZOJ3514]Codechef MARCH14 GERALD07加強版 題解 [LCT+主席樹]

[BZOJ3514]Codechef MARCH14 GERALD07加強版 題解 [LCT+主席樹]

技術標籤:practicepython生成器

迭代器和生成器

迭代器:

一、可迭代物件:

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
    

總結:

迭代器:
  1. 自定義的迭代器需要將魔法方法__ next __ 和__ iter __ 重構, __ iter __ 中返回的是迭代器物件,而 __ next __ 中編寫的則是迭代規則。
  2. 自定義的迭代器是一個類,應該按照類的操作呼叫
生成器:
  1. 使用了yield關鍵字的函式不再是函式,而是生成器。(使用了yield的函式就是生成器)
  2. yield關鍵字有兩點作用:
    • 儲存當前執行狀態(斷點),然後暫停執行,即將生成器(函式)掛起
    • 將yield關鍵字後面表示式的值作為返回值返回,此時可以理解為起到了return的作用
  3. 可以使用next()函式讓生成器從斷點處繼續執行,即喚醒生成器(函式)
  4. Python3中的生成器可以使用return返回最終執行的返回值,而Python2中的生成器不允許使用return返回一個返回值(即可以使用return從生成器中退出,但return後不能有任何表示式)。