guxh的python筆記:迭代
1,可迭代物件iterator,迭代器iterator,生成器generator
可迭代物件iterable:實現了__iter__方法的類,iter()方法可以從iterable中獲取iterator。
迭代器Iterator:1)實現__iter__方法(返回自己)和__next__(獲取值)方法的類。2)生成器。
生成器generator:含yeild,生成器屬於迭代器。
上述三種類型都可作用於for迴圈。
list、dict、str雖然是iterable,卻不是iterator,為什麼呢?因為iterator表示的是一個數據流,可以被next()呼叫不斷返回下一個資料,直到沒有資料時丟擲StopIteration錯誤。可以把資料流看作一個序列,但我們無法提前知道序列長度,只有不斷通過next()進行下一個計算。iterator甚至可以表示無限大的資料流,list不可能無限大。
2,可迭代物件與迭代器的關係
關於可迭代與迭代器的關係,具體實現細節可參考“3實現可迭代的方法”中的經典版方法。
s = 'abcdefg' # s是個iterator,<class 'str'>
s可以被迭代:
for i in s: print(i)
迭代的本質是從iterable獲取iterator(iterable的__iter__方法return了一個iterator),然後再不斷使用iterator的next()方法獲取值:
sit = iter(s) # it是個iterator,<class 'str_iterator'> while True: try: print(next(sit)) except StopIteration: break
判斷是否可迭代,iterable和iterator都可以被迭代:
isinstance(s, collections.Iterable) # True isinstance(sit, collections.iterable) # True
但s不能被next,sit可以被next,因為s是iterable沒有__next__,sit是iterator具有__next__
3,實現物件可迭代的方法
實現一個序列型別,接受輸入值x,返回從x到11的值。
3.1,方法一:python序列鴨子型別
python在嘗試迭代物件時,找不到__iter__就會去呼叫__getitem__,__getitem__實現從0開始的索引取值即可
class Foo: def __init__(self, data): self.data = data def __getitem__(self, i): return range(self.data, 11)[i]
3.2,方法二:經典版
iterable+iterator,構建Foo的iterator,缺點是程式碼量大。
關鍵點:iterable的__iter__返回iterator;iterator的__iter__返回self,__next__逐個取值。
class Foo: def __init__(self, data): self.data = data def __iter__(self): # iterable中的__iter__返回iterator return Foo_iterator(self.data) class Foo_iterator: def __init__(self, data): self.data = data def __iter__(self): # iterator中的__iter__返回自己 return self def __next__(self): # iterator實現__next__ if self.data > 10: raise StopIteration else: num = self.data self.data += 1 return num
看看Foo的例項和返回的iterator:
f = Foo(1) fit = iter(f) print(type(f)) # <class '__main__.Foo'> print(type(fit)) # <class '__main__.Foo_iterator'>
對比下內建的iterable類,是不是完全一致:
s = 'abc' sit = iter(s) print(type(s)) # <class 'str'> print(type(sit)) # <class 'str_iterator'> r = range(10) rit = iter(r) print(type(r)) # <class 'range'> print(type(rit)) # <class 'range_iterator'>
3.3,方法三:糟糕版
Foo自己實現__next__和__iter__,讓Foo既是iterable,也是自己的iterator,糟糕不推薦
class Foo: def __init__(self, data): self.data = data def __iter__(self): return self def __next__(self): if self.data > 10: raise StopIteration else: num = self.data self.data += 1 return num
3.4,方法四:generator版
用generator實現iterable中的__iter__方法
class Foo: def __init__(self, data): self.data = data def __iter__(self): for i in range(self.data, 11): yield i
看看Foo的例項和返回的iterator,發現iter()返回了一個generator:
f = Foo(1) fit = iter(f) print(type(f)) # <class '__main__.Foo'> print(type(fit)) # <class 'generator'>
備註:Foo中的__iter__獲取資料時,用的是惰性獲取range(非惰性就是list(range(self.data, 11)))。一般推薦用惰性函式實現,例如用finditer替代findall。
3.5,方法五:生成器表示式
class Foo: def __init__(self, data): self.data = data def __iter__(self): return (i for i in range(self.data, 11))
4,生成器函式
待補充