python可迭代对象,迭代器,生成器
-
容器是一系列元素的集合,str、list、set、dict、file、sockets對象都可以看作是容器,容器都可以被迭代(用在for,while等語句中),因此他們被稱為可迭代對象。
-
可迭代對象實現了__iter__方法,該方法返回一個迭代器對象。
-
迭代器持有一個內部狀態的字段,用于記錄下次迭代返回值,它實現了__next__和__iter__方法,迭代器不會一次性把所有元素加載到內存,而是需要的時候才生成返回結果。
-
生成器是一種特殊的迭代器,它的返回值不是通過return而是用yield。
一,迭代器與可迭代對象?
迭代器:是訪問數據集合內元素的一種方式,一般用來遍歷數據,但是他不能像列表一樣使用下標來獲取數據,也就是說迭代器是不能返回的。
Iterator:迭代器對象,必須要實現next魔法函數
Iterable:可迭代對象,繼承Iterator,必須要實現iter魔法函數
上面的例子中a是一個列表,也是一個可迭代對象,那么如何才能讓這個a變成迭代器呢?使用iter()即可。
from collections import Iterable,Iterator a = [1,2,3] a = iter(a) print(isinstance(a,Iterator)) print(isinstance(a,Iterable)) print(next(a)) print('----') for x in a:print(x)可以看到現在a是可迭代對象又是一個迭代器,說明列表a中有iter方法,該方法返回的是迭代器,這個時候使用next就可以獲取a的下一個值,但是要記住迭代器中的數值只能被獲取一次。
梳理迭代器(Iterator)與可迭代對象(Iterable)的區別:
可迭代對象:繼承迭代器對象,可以用for循環(說明實現了iter方法)
迭代器對象:可以用next獲取下一個值(說明實現了next方法),但是每個值只能獲取一次,單純的迭代器沒有實現iter魔法函數,所以不能使用for循環
只要可以用作for循環的都是可迭代對象
只要可以用next()函數的都是迭代器對象
列表,字典,字符串是可迭代對象但是不是迭代器對象,如果想變成迭代器對象可以使用iter()進行轉換
Python的for循環本質上是使用next()進行不斷調用,for循環的是可迭代對象,可迭代對象中有iter魔法函數,可迭代對象繼承迭代器對象,迭代器對象中有next魔法函數
一般由可迭代對象變迭代器對象
二,可迭代對象?
可迭代對象每次使用for循環一個數組的時候,本質上會從類中嘗試調用iter魔法函數,如果類中有iter魔法函數的話,會優先調用iter魔法函數,當然這里切記iter方法必須要返回一個可以迭代的對象,不然就會報錯。
如果沒有定義iter魔法函數的話,會創建一個默認的迭代器,該迭代器調用getitem魔法函數,如果你沒有定義iter和getitem兩個魔法函數的話,該類型就不是可迭代對象,就會報錯。
class s:def __init__(self,x):self.x = xdef __iter__(self):return iter(self.x)# 這里必須要返回一個可以迭代的對象# def __getitem__(self, item):# return self.x[item] # iter和getitem其中必須要實現一個 a = s('123') # 這里的a就是可迭代對象 # 這里不能調用next(a)方法,因為沒有定義 for x in a:print(x)三,迭代器
一開始提起,iter搭配Iterable做可迭代對象,next搭配Iterator做迭代器。next()接受一個迭代器對象,作用是獲取迭代器對象的下一個值,迭代器是用來做迭代的,只會在需要的時候產生數據。
和可迭代對象不同,可迭代對象一開始是把所有的列表放在一個變量中,然后用getitem方法不斷的返回數值,getitem中的item就是索引值。
但是next方法并沒有索引值,所以需要自己維護一個索引值,方便獲取下一個變量的位置。
class s:def __init__(self,x):self.x = x# 獲取傳入的對象self.index = 0# 維護索引值def __next__(self):try:result = self.x[self.index]# 獲取傳入對象的值except IndexError:# 如果索引值錯誤raise StopIteration# 拋出停止迭代self.index += 1# 索引值+1,用來獲取傳入對象的下一個值return result# 返回傳入對象的值a = s([1,2,3]) print(next(a)) print('----------') for x in a: #類中并沒有iter或者getitem魔法函數,不能用for循環,會報錯print(x)上面一個就是完整的迭代器對象,他是根據自身的索引值來獲取傳入對象的下一個值,并不是像可迭代對象直接把傳入對象讀取到內存中,所以對于一些很大的文件讀取的時候,可以一行一行的讀取內容,而不是把文件的所有內容讀取到內存中。
這個類是迭代器對象,那么如何才能讓他能夠使用for循環呢?那就讓他變成可迭代對象,只需要在類中加上iter魔法函數即可。
class s:def __init__(self,x):self.x = x# 獲取傳入的對象self.index = 0# 維護索引值def __next__(self):try:result = self.x[self.index]# 獲取傳入對象的值except IndexError:# 如果索引值錯誤raise StopIteration# 拋出停止迭代self.index += 1# 索引值+1,用來獲取傳入對象的下一個值return result# 返回傳入對象的值def __iter__(self):return self a = s([1,2,3]) print(next(a)) print('----------') for x in a:print(x)根據上面的代碼提示,得到規律:
iter讓類變成可迭代對象,next讓類變成迭代器(要維護索引值)。
可迭代對象可以用for循環,迭代器可以用next獲取下一個值。
迭代器如果想要變成可迭代對象用for循環,就要在迭代器內部加上iter魔法函數
可迭代對象如果想要能用next魔法函數,使用自身類中的iter()方法即可變成迭代器對象
這個時候是不能再用next方法了,應為類b是一個可迭代對象,并非迭代器,這個時候不能用next方法,但是可以讓類b繼承類s,這樣就能用next()方法獲取下一個值,但是你的類b中要存在索引值,不然會報錯,如下代碼:
class s:def __init__(self,x):self.x = x# 獲取傳入的對象self.index = 0# 維護索引值def __next__(self):try:result = self.x[self.index]# 獲取傳入對象的值except IndexError:# 如果索引值錯誤raise StopIteration# 拋出停止迭代self.index += 1# 索引值+1,用來獲取傳入對象的下一個值return result# 返回傳入對象的值# def __iter__(self):# return self class b(s):def __init__(self,x):self.x = xself.index = 0def __iter__(self):return s(self.x) a = b([1,2,3])print(next(a)) print(next(a))可以這么做,但是沒必要,因為這樣違反了設計原則。
迭代器模式:提供一種方法順序訪問一個聚合對象中的各種元素,而又不暴露該對象的內部表示。
迭代器的設計模式是一種經典的設計模式,根據迭代器的特性(根據索引值讀取下一個內容,不一次性讀取大量數據到內存)不建議將next和iter都寫在一個類中去實現。
新建一個迭代器,用迭代器維護索引值,返回根據索引值獲取對象的數值,新建另一個可迭代對象,使用iter方法方便的循環迭代器的返回值。
四,生成器
生成器:函數中只要有yield,這個函數就會變成生成器。每次運行到yield的時候,函數會暫停,并且保存當前的運行狀態,返回返回當前的數值,并在下一次執行next方法的時候,又從當前位置繼續往下走。生成器其實是一種特殊的迭代器,不過這種迭代器更加優雅。它不需要再像上面的類一樣寫__iter__()和__next__()方法了,只需要一個yiled關鍵字。
def gen():yield 1# 返回一個對象,這個對象的值是1 def ret():return 1# 返回一個數字1 g = gen() r = ret() print(g,r) print(next(g))可以看到return是直接返回數值1,yield是返回的一個生成器對象,這個對象的值是1,使用next(g)或者for x in g:print x 都是可以獲取到他的內容的,這個對象是在python編譯字節碼的時候就產生。
def gen():yield 1yield 11yield 111yield 1111yield 11111yield 111111# 返回一個對象,這個對象內的值是1和11,111... def ret():return 1return 3# 第二個return是無效的 g = gen() r = ret() print(g,r) print(next(g)) for x in g:print(x)就像迭代器的特性一樣,獲取過一遍的值是沒法再獲取一次的,并且不是那種一次把所有的結果求出放在內存或者說不是一次性讀取所有的內容放在內存中。
梳理特性:
使用yield的函數都是生成器函數
可以使用for循環獲取值,也可以使用next獲取生成器函數的值
?例子1:迭代器和生成器實現斐波那契數列
class Fib:def __init__(self):self.prev = 0self.curr = 1def __iter__(self):return selfdef __next__(self):value = self.currself.curr += self.prevself.prev = valuereturn valuef = Fib() for i in range(10):print(next(f)) # list(islice(f, 0, 10))def fib():prev, curr = 0, 1while True:yield currprev, curr = curr, curr + prevf = fib() for i in range(10):print(next(f))例子2:利用生成器產生batch的圖片
import pandas as pd import numpy as np import cv2 def make_csv():images_path=np.array([str(i)+'.jpg' for i in np.random.randint(0,1000,18)]).reshape(-1,1)# print(images_path)label = np.random.randint(0,2,18).reshape(-1,1)# print(label)res = np.hstack((images_path,label))print(res)res_df=pd.DataFrame(res)res_df.to_csv('test.csv',index=False,header=None)def yield_batch():path='./test.csv'res=np.array(pd.read_csv(path))print('res.shape:',res.shape)datas=res[:,0]labels=res[:,-1]# print('datas:',datas)# print('labels:',labels)batch_size=8num_batch=len(datas)//batch_sizefor i in range(num_batch):imgs=[]train_datas = datas[batch_size*i:batch_size*(i+1)]train_lables = labels[batch_size*i:batch_size*(i+1)]for img_path in train_datas:img=1# img = cv2.imread(img_path)# img = cv2.resize(img,500)# img = img/255 #歸一化處理imgs.append(img)yield np.array(imgs),np.array(train_lables)if __name__ == '__main__':# make_csv()epochs=2for i in range(epochs):print('======={} epoch==========='.format(i))for (data,label) in yield_batch():print('data:',data)print('label:',label)總結
以上是生活随笔為你收集整理的python可迭代对象,迭代器,生成器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python(c++)刷题+剑指offe
- 下一篇: websocket python爬虫_p