<!-- TOC -->python
第14章 可迭代的對象、迭代器和生成器python2.7
<!-- /TOC -->code
舉個例子:orm
import re re_word = re.compile(r'\w+') class Sentence(object): def __init__(self, text): self.text = text self.word = re_word.findall(text) def __getitem__(self, item): return self.word[item] def __len__(self): return len(self.word) def __str__(self): return 'Sentence(%s)' % self.word if __name__ == "__main__": s = Sentence("Today is Tuesday") print(s) for word in s: print(word)
返回結果:協程
Sentence(['Today', 'is', 'Tuesday']) Today is Tuesday
咱們知道一個對象能夠迭代是由於實現了__iter__
方法,
可是在Sentence中並無實現__iter__
方法。那爲何能夠迭代呢。對象
緣由在於在python中實現了iter和getitem的都是可迭代的。首先會檢查是否實現了iter方法,若是實現了則調用,若是沒有可是實現了__getitem__
方法。blog
Python就會建立一個迭代器。索引
嘗試按照順序獲取元素。若是嘗試失敗則會拋出typeerror異常,提示object is not iterable.
所以:
若是對象實現了能返回迭代器的__iter__
方法,那麼對象就是可迭代的。若是實現了
__getitem__
方法,並且其參數是從零開始的索引。
這種對象也能夠迭代。
咱們用__iter__
方法來改造以前的Sentence。
在__iter__
中返回一個可迭代對象iter(self.word)。
當執行for word in s的時候就會調用__iter__
方法
import re re_word = re.compile(r'\w+') class Sentence(object): def __init__(self, text): self.text = text self.word = re_word.findall(text) def __iter__(self): return iter(self.word) def __len__(self): return len(self.word) def __str__(self): return 'Sentence(%s)' % self.word if __name__ == "__main__": s = Sentence("Today is Tuesday") print(s) for word in s: print(word)
再來看下next, next的做用是返回下一個元素,若是沒有元素了,拋出stopIteration異常。
咱們來看下next的使用方法。若是要遍歷一個字符串,最簡單的方法以下:
s = 'abc' for char in s: print(char)
若是不用for方法,代碼須要修改以下:
s = 'abc' it = iter(s) while True: try: print(next(it)) except StopIteration: del it break
首先將s變成一個iter對象,而後不斷調用next獲取下一個字符,若是沒有字符了,則會拋出StopIteration異常釋放對it的引用
s = 'abc' it = iter(s) print(next(it)) print(next(it)) print(next(it)) print(next(it)) # StopIteration
由於只有3個字符,可是調用了4次it.next()致使已經找不到字符所以拋出異常。
總結:
可迭代對象:實現了__iter__
方法,就是可迭代的,能夠返回自身做爲迭代器。
也能夠返回其餘一個可迭代對象
迭代器:在Python2中實現了next方法,在python3中實現了__next__
方法。
首先要讓x經過iter變成一個可迭代對象,而後使用迭代器來調用元素
使用迭代器好處就是:每次只從對象中讀取一條數據,不會形成內存的過大開銷。
能夠看看它的內存佔用:
使用python2.7.15+:
import sys i = iter(range(1000000)) print sys.getsizeof(i) r = range(1000000) print sys.getsizeof(r) y = xrange(1000000) # 注意 xrange跟range的區別: xrange是range的迭代器的表達方式 print sys.getsizeof(y)
結果返回:
56 8000064 32
使用python 3.6+
import sys i = iter(range(1000000)) print (sys.getsizeof(i)) r = range(1000000) print (sys.getsizeof(r))
返回結果:
32 48
這裏有個問題:爲何在python2跟Python3的運行結果相差這麼大呢?
這是由於python3內部機制已經將range轉換成了一個迭代器了。
這裏能夠看的出來適合大的數據,好比好幾個G的數據, 使用了迭代器 內存使用大幅度減小,這是迭代器最大的優勢。
總結下:
咱們簡單說迭代器就是訪問集合元素,迭代器就是有一個next()方法的對象,而不是經過索引來計數的。
那麼咱們怎麼能訪問迭代器裏面的元素呢?
迭代器有兩個方法 ,分別是iter()和next()方法
這兩個方法是迭代器最基本的方法
一個用來得到迭代器對象,一個用來獲取容器中的下一個元素。
itertools是python提供的很是高效建立與使用迭代器的模塊
from itertools import chain test = chain.from_iterable('ABCDEFG') # print(test) # print(dir(test)) # 查看類具體方法 print(test.__next__()) # 'A' print(test.__next__()) # 'B' print(test.__next__()) # 'C' test2 = chain('AB', 'CDE', 'F') print(list(test2)) # ['A', 'B', 'C', 'D', 'E', 'F']
咱們知道迭代器是不支持索引的,緣由就是索引需實現明元素確佔用的內存地址,而迭代器是用到元素的時候纔會建立。以下:
i = iter(range(3)) # 建立迭代器 # i.index(2) # 獲取元素爲2的索引 # AttributeError: 'range_iterator' object has no attribute 'index' # 列表 l = range(3) print(l.index(2)) # 獲取索引2
這個時候可使用內建函數enumerate(),這個函數很重要。
for i, j in enumerate(iter(['A', 'B', 'C'])): # for i, j in enumerate(['A', 'B', 'C']): print(i, j)
運行結果返回:
0 A 1 B 2 C
能夠看下這個函數的源碼是怎麼寫的
class enumerate(Iterator[Tuple[int, _T]], Generic[_T]): def __init__(self, iterable: Iterable[_T], start: int = ...) -> None: ... def __iter__(self) -> Iterator[Tuple[int, _T]]: ... if sys.version_info >= (3,): def __next__(self) -> Tuple[int, _T]: ... else: def next(self) -> Tuple[int, _T]: ..
生成器是迭代器,但你只能遍歷它一次(iterate over them once)
由於生成器並無將全部值放入內存中,而是實時地生成這些值
mygenerator = (x * x for x in range(3)) # 生成器 # mygenerator = [x * x for x in range(3)] # 列表 for i in mygenerator: print("i=", i) for i in mygenerator: print("New=", i)
運行結果:
i= 0 i= 1 i= 4
注意,你不能執行for i in mygenerator第二次,由於每一個生成器只能被使用一次
在Python中,使用生成器能夠很方便的支持迭代器協議。
生成器經過生成器函數產生,生成器函數能夠經過常規的def語句來定義,可是不用return返回,
而是用yield一次返回一個結果,在每一個結果之間掛起和繼續它們的狀態,來自動實現迭代協議。
也就是說,yield是一個語法糖,內部實現支持了迭代器協議
同時yield內部是一個狀態機,維護着掛起和繼續的狀態。
下面看看生成器的使用:
def Zrange(n): i = 0 while i < n: yield i i += 1 zrange = Zrange(3) print(zrange) # <generator object Zrange at 0x000002997DE75468> print([i for i in zrange]) # [0, 1, 2]
在這個例子中,定義了一個生成器函數,函數返回一個生成器對象,而後就能夠經過for語句進行迭代訪問了。
其實,生成器函數返回生成器的迭代器。 「生成器的迭代器」這個術語一般被稱做」生成器」。
要注意的是生成器就是一類特殊的迭代器。做爲一個迭代器,生成器必需要定義一些方法,其中一個就是next()。
如同迭代器同樣,咱們可使用next()函數來獲取下一個值。
例子:
def Zrange(n): print("begin of Zrange") i = 0 while i < n: print("before yield:", i) yield i i += 1 print("after yield:", i) print("begin of Zrange") zrange = Zrange(3) # print(zrange) print(zrange.__next__()) print("-" * 10) print(zrange.__next__()) print("-" * 10) print(zrange.__next__()) # print(zrange.__next__()) # StopIteration
運行結果:
begin of Zrange before yield: 0 0 ---------- after yield: 1 before yield: 1 1 ---------- after yield: 2 before yield: 2 2
經過結果能夠看到:
總結:生成器是迭代器的一種,但功能方法比迭代器多
生成器的建立可使用yield關鍵字, 也可使用生成器表達式
(x*2 for i in range(10))
判斷是否爲生成器函數可用isgeneratorfunction, 判斷是否爲生成器對象可用isgenerator
from inspect import isgeneratorfunction, isgenerator g = (i for i in range(3)) print(isgenerator(g)) # True def demo(): yield 1 print(isgenerator(demo())) # True print(isgeneratorfunction(demo())) # False print(isgeneratorfunction(demo)) # True
def coroutine(): print("coroutine start...") result = None while True: s = yield result result = 'result:{}'.format(s) c = coroutine() c.send(None) # coroutine start... print(c.send("first")) # result:first print(c.send("second")) # result:second c.close() # c.send("hello") # StopIteration