用一種方法,最好是只有一種方法來做一件事
迭代器的用途:
- for ... in 循環
- 遍歷文件等
- 列表推導,字典推導,集合推導
- 元組拆包
- 調用函數時,使用* 拆包實參
可迭代對象
首先我們來分析一下序列可迭代的原因:iter函數
解釋器需要迭代對象x時,會自動調用iter(x)
,內置的iter
有如下作用
- 檢查對象是否實現了
__iter__
方法,如果實現了就調用他,獲取一個迭代器
def __iter__(self):
return iter(self._components)
- 如果沒有實現,但是實現了
__getitem__
,Python會創建一個迭代器,嘗試依次獲取元素
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, numbers.Integral):
return self._components[index]
else:
msg = '{.__name__} indices must be integers'
raise TypeError(msg.format(cls))
- 如果失敗了,就會拋出TypeError異常
從 Python 3.4 開始,檢查對象 x 能否可迭代,最準確的方法是:調用 iter(x) 函 數,如果不可迭代,再處理 TypeError 異常。這比使用 isinstance(x, abc.Iterable) 更準確,因為 iter(x) 函數會考慮到遺留的 getitem 方法, 而 abc.Iterable 類則不考慮。
只要實現了上述任意一個方法,我們就可以說該對象是可迭代對象
迭代器
若一個類想成為迭代器需實現
- 方法
__next__
__iter__
使用:
it = iter(obj)
while True:
try:
print(next(it))
except StopIteration:
del it
break
檢 查對象 x 是否為迭代器最好的方式是調用 isinstance(x, abc.Iterator)。得益 于 Iterator.subclasshook 方法,即使對象 x 所屬的類不是 Iterator 類的 真實子類或虛擬子類,也能這樣檢查。
有同學看到這里就不禁疑問,迭代器和可迭代對象,到底有啥區別 ???
可迭代的對 象有個 iter 方法,每次都實例化一個新的迭代器;而迭代器要實現 next 方 法,返回單個元素,此外還要實現 iter 方法,返回迭代器本身。 因此,迭代器可以迭代,但是可迭代的對象不是迭代器。
那我能不能將一個類實現為即是可迭代對象又是迭代器呢? 可能我們都不會意識到 這叫反模式
迭代器模式的適用性總結:
- 訪問一個聚合對象的內容而無需暴露它的內部表示
- 支持對聚合對象的多種遍歷
- 為遍歷不同的聚合結構提供一個統一的接口(即支持多態迭代)
為了“支持多種遍歷”,必須能從同一個可迭代的實例中獲取多個獨立的迭代器,而且各個 迭代器要能維護自身的內部狀態,因此這一模式正確的實現方式是,每次調用 iter(my_iterable) 都新建一個獨立的迭代器。這就是為什么需要定義 迭代器類。
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
return SentenceIterator(self.words)
class SentenceIterator:
def __init__(self, words):
self.words = words
self.index = 0
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
def __iter__(self):
return self
在很多情況下,迭代器的遍歷實現可以使用生成器來實現
生成器
生成器函數的工作原理
由一個生成器函數返回一個生成器對象,該函數使用yield
關鍵字產出數據元素,一般情況下,一個生成器函數包含一個不斷yield
的循環,每次迭代都會訪問下一個yield的產出數據
def __iter__(self):
for match in RE_WORD.finditer(self.text): # <2>
yield match.group()
def geb_AB():
print("start")
yield "A"
print("continue")
yield "B"
print("end")
列表推導
[x*3 for x in gen_AB()]
生成器表達式
res = (x*3 for x in gen_AB()) # 僅僅返回一個生成器
只有在for 循環迭代的時候,才會真正執行gen_AB的函數體,每次執行一個yield
yield from
如果生成器函數需要產出另外一個生成器的值,一般會使用嵌套的for循環解決
def chain(*iterable):
for it in iterable:
for v in it:
yield v
s = "abcde"
t = tuple(2,4,5,6,6,7,)
list(chain(s,t))
如果是使用yield from
def chain(*iterable):
for it in iterable:
yield from it
iter 函數
iter函數有個極少使用的方式:接收兩個入參,第一個是可調用對象或者函數,第二個為哨兵符,遇到到哨兵符就會拋出StopIteration
.
def random_s():
return randint(1,6)
d_it = iter(random_s, 1) # 當遇到1時停止
for roll in d_it:
print(roll)
with open('mydata.txt') as fp:
for line in iter(fp.reandline, "\n"):
process_line(line)
下面貼幾張標準庫生成器函數