Python系列(三):關於迭代器和生成器,你該瞭解這些
一.前言
說起迭代器和生成器,可以說是python語言的精髓之一,生成器可能有人沒用過,但是迭代器絕對是大家經常使用的(可能你並不瞭解自己正在使用迭代器),最常見的迭代器使用場景就是我們用for迴圈來遍歷各種列表,字串,元組等各種物件了。
mlist = [1,3,5,7,9,2,4,6,8,10]
for item in mlist:
print(item)
string = 'hello world'
for s in string:
print(s)
mtuple = ('c','c++','java','python')
for item in mtuple:
print(item)
怎麼樣?是不是感覺很熟悉?而對於另一個生成器而言,我先不說太多,你第一印象只需記住它實質上也是一種迭代器,但它更簡潔。下面我將深入的為你剖析迭代器和生成器。
二.迭代器
2.1.迭代器物件(iterators)和可迭代物件(iterable)
2.1.1.辨析迭代器物件和可迭代物件:
- 迭代器物件中定義了__next__()方法,每次呼叫該方法才會從容器中取出一個元素,當容器沒有元素可取時會丟擲StopIteration異常。
- 可迭代物件中定義了__iter__()方法,該方法可以返回一個迭代器物件。
- 迭代器是一次性的,迭代器從第一個元素開始,直到所有的元素被訪問完,迭代器在訪問過程中只能往前不能退後。
- 可迭代物件可以多次使用,每次使用iter()方法便可以從可迭代物件中返回一個迭代器。
頭兩點是區分迭代器物件和可迭代物件最好的方法,我們可以這樣理解:迭代器物件和可迭代物件都是用來迭代的,但迭代器物件可以直接通過__next__()方法進行物件中元素的迭代,而可迭代物件需要先使用__iter()__方法返回一個迭代器物件,然後就可以使用迭代器物件中的__next__()方法進行遍歷了。
程式碼示例:
#在python中內建了iter()和next()函式,當使用這兩個函式時會分別呼叫__iter__(),__next__()方法。
mlist = [1,3,5,7]
print(next(mlist))
'''
直接使用該行程式碼會報錯:TypeError: 'list' object is not an iterator
因為mlist是一個列表,而列表不是一個迭代器並沒有實現__next__()方法
'''
#呼叫iter方法返回一個可迭代物件
my_itera = iter(mlist)
print(type(my_itera))
#<class 'list_iterator'>
print(next(my_itera))
#1
print(next(my_itera))
#3
print(next(my_itera))
#5
print(next(my_itera))
#7
print(next(my_itera))
#列表中已經沒有元素了,丟擲異常:StopIteration
2.1.2.常見的可迭代物件
在python中最常見的可迭代物件有:列表,元組,字串,集合,字典。
2.1.3.為什麼for迴圈能迭代可迭代物件
這裡直接給出官方的解釋:Behind the scenes, the for statement calls iter() on the container object. The function returns an iterator object that defines the method __next__() which accesses elements in the container one at a time. When there are no more elements, __next__() raises a StopIteration exception which tells the for loop to terminate.
這段話的大概意思是:for迴圈自動對可迭代物件呼叫了__iter__()方法返回一個迭代器物件,每次迴圈時又對該迭代器物件呼叫__next__()方法獲取容器中的一個元素,當容器中沒有元素時會丟擲StopIteration異常以終止迴圈。
2.2.如何自定義迭代器類
2.2.1.定義方法
前面我們已經介紹了:可迭代物件(Iterable)需要實現__iter__()方法,迭代器(iterator)需要實現__next__()方法。因此,通常是先實現迭代器類,然實現可迭代物件類,但這樣可能比較麻煩,因此如今基本上是同時在一個類中同時定義__iter__()和__next__()兩個方法,這樣__iter__()方法只需要返回本身就可以了。
2.2.2.程式碼示例
#定義一個輸出斐波那契數列的迭代器
class Fibonacci(object):
"""define a iterator class about fibnacci"""
def __init__(self,num):
self.a = 0
self.b = 1
self.num = num
#定義__iter__方法
def __iter__(self):
return self
#定義__next__方法
def __next__(self):
if self.a > self.num:
raise StopIteration
else:
self.a,self.b = self.b,self.a + self.b
return self.a
f = Fibonacci(10000)#指定數列的最大值不超過10000
for item in f:
print(item,end=" ")
'''
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
'''
三.生成器
既然有了一般的迭代器那為什麼需要生成器呢?倘若你要自定義迭代器每次都要定義__iter__()方法和__next()__方法,這樣有點太麻煩了,但使用生成器卻不一樣,它的實現方式十分的簡潔,因為生成器會自動實現__iter__()和__next__()方法,並會在容器中沒有元素時自動丟擲StopIteration異常。這樣一來,就可以在減少程式碼量的情況下實現迭代器的功能。
3.1.什麼是生成器
這裡先給貼出一段程式碼,下面這段程式碼便實現了一個生成器(函式Fibonacci),同樣是實現一個可以用來迭代的物件,使用生成器來建立卻如此的簡潔。
def Fibonacci(num):
a,b=1,1
while b < num:
a,b = b,a+b
yield a
f = Fibonacci(10000)
for item in f:
print(item,end=" ")
'''
1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
'''
對於生成器的準確定義,這裡仍然引用官方文件的原話:Generators are a simple and powerful tool for creating iterators. They are written like regular functions but use the yield statement whenever they want to return data. 提煉出來的大致意思是:生成器是使用yield關鍵字來代替return的函式。
3.2.yield關鍵字
yield與return的第一個不同之處是:yield返回的並不是值,而是一個生成器,這裡給出程式碼示例為:
def testYield(n):
yield n
my_gen = testYield(10)
print(my_gen)
print(next(my_gen))
'''執行結果
<generator object testYield at 0x000001BB551C4BF8>
10
'''
第二個不同之處是:return關鍵字會立刻返回值然後結束函式,但在包含yield的函式(生成器)中,每次執行到yield函式都會暫停並儲存當前所有的執行資訊,並在下一次執行next()方法時從上一次暫停的位置繼續執行下去。程式碼示例為:
def testYield(n):
for i in range(n):
print('before yield')
yield i
print('after yield')
my_gen = testYield(10)
print(next(my_gen))
'''
before yield
0
'''
print(next(my_gen))
'''
after yield
before yield
1
'''
print(next(my_gen))
'''
after yield
before yield
2
'''
3.4.如何建立一個生成器
- 從上面可以看出,在函式中使用yield就可以實現一個生成器,注意yield只能在函式中使用。
- Python還提供了一種生成器的實現方式,這種方式類似於列表解析式,但使用圓括號"()“代替了方括號”[]",與列表解析式不同的是,生成器像列表解析式一樣一次性生成所有的元素的,而是一次生成一個,當你需要下一個時才會生成下一個,這種方式可以節約記憶體資源。
下面只演示第二種建立方式(第一種前面有栗子)
my_gen = (x**2 for x in range(1000000))
print(my_gen)
#<generator object <genexpr> at 0x000001BADC3F4BF8>
print(next(my_gen))
#0
print(next(my_gen))
#1
print(next(my_gen))
#4
print(next(my_gen))
#9
對於生成器,我在這裡推薦一個問題:列表展平問題,即將類似於[1,2,3,[4,5],[7,8]],[1,2,3,4,5,6,[[7,8,9,[10,11]]],[[[[12]]]]]這樣的列表展成維度為1的的列表,例如上述兩例分別展平為[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7,8,9,10,11,12]有興趣的可以看看自己能不能用生成器解決這個問題。
四.結語
善用迭代器和生成器可以幫助你寫出更簡潔,更漂亮的python程式碼。當然迭代器和生成器的用法遠遠不止如此,有要更深入瞭解的可以自行探索。
相關文章
- 關於存款保險,您應該瞭解這些IWQ
- 關於python中可迭代物件和迭代器的一些理解Python物件
- 關於資訊保安,這些你都瞭解嗎?
- 關於我對可迭代物件,迭代器,生成器的一些理解物件
- 學習Python,這些你瞭解嗎?Python
- 1.5.4 Python迭代器和生成器Python
- 初識Python,我想你應該瞭解這些...Python
- 詳解python三大器——迭代器、生成器、裝飾器Python
- 關於Linux系統,這些知識你都瞭解嗎?Linux
- Java8新特性,你應該瞭解這些!Java
- python3.7 迭代器和生成器Python
- 關於微服務,這些你都瞭解嗎-微服務介紹微服務
- 中科三方:關於SSL證書你應該知道這些事
- python 生成器&迭代器Python
- Python——你應該知道這些Python
- Python之裝飾器、迭代器和生成器Python
- 小研帶你學Python(三):關於變數,這些知識你應該要了解一下Python變數
- 草根學Python(七) 迭代器和生成器Python
- python迭代器和生成器的總結Python
- 關於“等保測評”的這些疑問,你都瞭解嗎?
- 迭代器和生成器
- Python迭代器與生成器Python
- Python之可迭代物件、迭代器、生成器Python物件
- [譯]關於NODE_ENV,哪些你應該瞭解
- Python學習筆記 - 迭代器和生成器Python筆記
- 簡單介紹python迭代器和生成器Python
- 這份Python標準異常表你應該瞭解!Python
- 建立索引,這些知識應該瞭解索引
- Python迭代器&生成器&裝飾器Python
- Python語法—迭代器、生成器Python
- 迭代器,生成器(generator)和Promise的“微妙”關係Promise
- Python學習之路34-迭代器和生成器Python
- 利用大資料做好消費者運營,你該瞭解這些大資料
- excalidraw 這些用法你肯定不瞭解
- python_裝飾器——迭代器——生成器Python
- PHP的迭代器和生成器PHP
- Python學習之三大名器-裝飾器、迭代器、生成器Python
- 史上最全 Python 迭代器與生成器Python