python 中內置的可迭代的對象有 list、tuple、set、dict 等,那么我們自己怎么定義一個可迭代的對象呢?先來段代碼吧
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 __getitem__(self, item):
return self.words[item]
def __iter__(self):
for word in self.words:
yield word
sen = Sentence("abc def 123 456")
for i in sen:
print(i)
運行這段代碼,結果如下:
E:\python36\python3.exe E:/python_demo/test1.py
abc
def
123
456
Process finished with exit code 0`
通過結果分析,我們已經(jīng)看出 Sentence 對象已經(jīng)是可迭代的了!
此時我們可以注釋掉 __iter__ 函數(shù)
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __getitem__(self, item):
return self.words[item]
# def __iter__(self):
# for word in self.words:
# yield word
再次運行發(fā)現(xiàn)結果正常,接著注釋 __getitem__ 函數(shù)
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
# def __getitem__(self, item):
# return self.words[item]
# def __iter__(self):
# for word in self.words:
# yield word
發(fā)現(xiàn)報錯了,報錯信息很明朗,提示 Sentence 不是可迭代的對象
Traceback (most recent call last):
File "E:/python_demo/fluency_python/chapter14/test1.py", line 21, in <module>
for i in sen:
TypeError: 'Sentence' object is not iterable
Process finished with exit code 1
看到這,是不是可以說,對象只需要實現(xiàn) __getitem__ 函數(shù),那么該對象就是可迭代的呢?先別下定論,我們接著放開注釋掉的 __iter__ 函數(shù)
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
# def __getitem__(self, item):
# return self.words[item]
def __iter__(self):
for word in self.words:
yield word
發(fā)現(xiàn)運行結果又正確了,我們發(fā)現(xiàn) __iter__ 和 __getitem__ 中實現(xiàn)任意一個,對象就是可迭代的,怎么解釋這種情況呢,Python 解釋器中有這樣的描述
解釋器需要迭代對象 x 時, 會自動調用 iter(x)。
內置的 iter 函數(shù)有以下作用。
(1) 檢查對象是否實現(xiàn)了 __iter__ 方法, 如果實現(xiàn)了就調用它, 獲取
一個迭代器。
(2) 如果沒有實現(xiàn) __iter__ 方法, 但是實現(xiàn)了 __getitem__ 方法,
Python 會創(chuàng)建一個迭代器, 嘗試按順序(從索引 0 開始) 獲取元素。
(3) 如果嘗試失敗, Python 拋出 TypeError 異常, 通常會提示“C object
is not iterable”(C 對象不可迭代) , 其中 C 是目標對象所屬的類。
任何 Python 序列都可迭代的原因是, 它們都實現(xiàn)了 __getitem__ 函數(shù)。 其實, 標準的序列都實現(xiàn)了 __iter__ 函數(shù), 因此你也應該這么做
總結
因此我們在自定義可迭代對象需要實現(xiàn) __iter__ 函數(shù);嚴格來講,我們還需要實現(xiàn) __getitem__ 函數(shù),這個函數(shù)的主要作用是當前對象可通過下標取值
另外,如果有一個未知的對象,我們怎么判斷該對象是否可迭代呢,兩種方式
-
iter(x)
這是最準確的判斷方式,傳入要判斷的對象,只要對象實現(xiàn)了__getitem__ 、 __iter__中的其中一個,該對象都是可迭代的,否則拋出TypeError: 'C' object is not iterable 異常;這兒有個區(qū)別,如果僅僅實現(xiàn)了 __getitem__ ,調用 iter(x) 返回的是迭代器對象,如果實現(xiàn)的是 __iter__ ,那么調用 iter(x) 返回的是生成器對象; -
isinstance(x, abc.Iterable)
該方式會自動忽略 __getitem__ 函數(shù),僅當對象實現(xiàn)了 __iter__ 函數(shù)才返回 True,其他一律返回 False