一、迭代
1、定義
迭代是指通過for循環遍歷對象每一個元素的過程。
二、可迭代對象(iterable)
1、定義
可迭代對象是定義了 __iter__()
方法或 __getitem__()
方法的類對象。
2、__iter__()
from collections import abc
class MyIterable:
def __iter__(self):
pass
my_iterable = MyIterable()
3、__getitem__()
from collections import abc
class MyIterable:
def __getitem__(self, item):
pass
my_iterable = MyIterable()
定義了__iter__()
或__getitem__()
方法後,對象就可以使用for循環遍歷了。當然,上面只是一個簡單的示例,什麼功能都沒有,實際情況一般基於Python的內置數據類型(int, list, string…)去寫。在引入迭代器之後,__iter__()
的功能一般是返回一個迭代器。
4、說明
很多文章喜歡拿 collections.abc.Iterable
對象和一個類對象比較,然後判斷這個類對象是否是可迭代對象。如:
from collections import abc
class MyIterable:
def __getitem__(self, item):
pass
my_iterable = MyIterable()
print(isinstance(my_iterable, abc.Iterable))
其實這是不完全正確的。因爲根據Python文檔, Iterable 只實現了__iter__()
方法,所以一個對象如果實現 了__getitem__()
方法,但是沒有實現__iter__()
方法,用這種方式判斷,該對象就會被判定爲不是可迭代對象,這是錯誤的。只要實現這兩個方法中的任何一個,都是可迭代對象,應該使用**iter()**方法。
三、迭代器
1、定義
實現了迭代器協議(__iter__()
, __next__()
)的類對象稱爲迭代器。迭代器是在Python 2.2引入的,因爲迭代器實現了__iter__()
方法,所以迭代器也屬於可迭代對象。
2、__iter__()
返回對象本身。
3、__next__()
返回對象的下一個元素,如果沒有下一個元素則拋出 StopIteration
異常。
# 模仿range()迭代器自定義一個迭代器
class MyRangeIterator:
def __init__(self, num):
self.num = num
self.index = 0 # 設置第一個元素
def __iter__(self):
return self
def __next__(self):
if self.index < self.num:
val = self.index
self.index += 1 # 爲了每次返回下一個元素,所以每次+1
return val
else:
raise StopIteration() # 如果沒有元素了則拋出StopIteration
mri = MyRangeIterator(5)
print([i for i in mri]) # [0, 1, 2, 3, 4]
print([i for i in mri]) # []
4、典型的可迭代對象與迭代器
典型的可迭代對象的 __iter__()
方法返一個新的迭代器實例,而迭代器的__iter__()
方法返回迭代器本身。
# 可迭代對象
class MyRange:
def __init__(self, num):
self.num = num
def __iter__(self):
return MyRangeIterator(self.num)
# 迭代器
class MyRangeIterator:
def __init__(self, num):
self.num = num
self.index = 0 # 設置第一個元素
def __iter__(self):
return self
def __next__(self):
if self.index < self.num:
val = self.index
self.index += 1 # 爲了每次返回下一個元素,所以每次+1
return val
else:
raise StopIteration() # 如果沒有元素了則拋出StopIteration
mr = MyRange(5)
print([i for i in mr]) # [0, 1, 2, 3, 4]
print([i for i in mr]) # [0, 1, 2, 3, 4]
四、迭代原理
解釋器需要迭代對象 x 時,每迭代一次:
(1)執行 iter(x)。
(2)檢查對象是否實現了__iter__
方法,如果實現了就調用它,獲取到一個迭代器iterator。
(3)獲取到迭代器之後,執行next(iterator)
。
(4)然後執行 iterator.__next__()
獲取元素。
(5)如果沒有元素了就拋出 StopIteration
。
五、生成器
1、定義
根據Python術語表,有:
(1)generator
包含 yield
表達式的函數或者生成器叫做生成器。生成器本質上是一個函數,也叫生成器函數(generator function)。
示例:
def gen_123():
yield 123
print(gen_123) #<function gen_123 at 0x00000269E04D6558>,生成器本質上是一個函數
(2)generator iterator
生成器生成的對象(generator object,簡稱生成器對象)稱爲“generator iterator”。爲什麼這樣命名呢?因爲生成器對象也是一個迭代器。
示例:
from collections import abc
def gen_123():
yield 123
print(gen_123) # <function gen_123 at 0x00000269E04D6558>
g = gen_123()
print(g) # <generator object gen_123 at 0x00000269E0446C48>,生成器對象
print(isinstance(g, abc.Iterator)) # True,生成器生成的對象也是一個迭代器
下面是個人理解(不一定正確):
a. generator iterator
這個名稱有點“怪”,咋一看,你不知道它所表達的意思到底是生成器還是迭代器,還有點拗口,可能官方想表達的是生成器生成的迭代器
。
b.有人喜歡把生成器稱爲“特殊的迭代器”,其實這是不對的,生成器本質是函數,而迭代器本質是類對象。生成器生成的對象纔是迭代器。
2、yield
關於yield的實現原理,可以參考這篇文章Python yield。本文只說yield的用法。
yield表達式 的語法如下:
yield_atom ::= "(" yield_expression ")"
yield_expression ::= "yield" [expression_list | "from" expression]
yield關鍵字的功能:
(1)返回給調用者一個值,並掛起當前狀態。
(2)當執行next(generator iterator
),從離開的地方繼續。
3、示例
class MyRange:
def __init__(self, num):
self.num = num
self.index = 0
def __iter__(self):
return self.my_range_generator()
def my_range_generator(self):
while self.index < self.num:
yield self.index
self.index += 1
my_range = MyRange(5)
for i in my_range:
print(i, end=' ') # 0 1 2 3 4
4、生成器表達式
生成器表達式的作用和生成器函數的作用也是一樣的,生成一個generator iterator
。所以也是一種生成器。
(1)語法
generator_expression ::= "(" expression comp_for ")"
(2)示例
g = (x*y for x in range(10) for y in range(x, x+10))
參考資料
[1]PEP234, iterator: https://www.python.org/dev/peps/pep-0234/
[2]PEP255, Simple Generators: https://www.python.org/dev/peps/pep-0255/
[3]PEP342, Coroutines via Enhanced Generators:https://www.python.org/dev/peps/pep-0342/