<b>8.1 可疊代對象(iterable)</b>
大部分對象都是可疊代,隻要實作了__iter__方法的對象就是可疊代的。
__iter__方法會傳回疊代器(iterator)本身,例如:
>>> lst = [1,2,3]
>>> lst.__iter__()
<listiterator object at 0x7f97c549aa50>
python提供一些語句和關鍵字用于通路可疊代對象的元素,比如for循環、清單解析、邏輯操作符等。
判斷一個對象是否是可疊代對象:
>>> from collections import iterable # 隻導入iterable方法
>>> isinstance('abc', iterable)
true
>>> isinstance(1, iterable)
false
>>> isinstance([], iterable)
這裡的isinstance()函數用于判斷對象類型,後面會講到。
可疊代對象一般都用for循環周遊元素,也就是能用for循環的對象都可稱為可疊代對象。
例如,周遊清單:
>>> lst = [1, 2, 3]
>>> for i in lst:
... print i
...
1
2
3
<b></b>
部落格位址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
qq群:323779636(shell/python運維開發群)
8.2 疊代器(iterator)
具有next方法的對象都是疊代器。在調用next方法時,疊代器會傳回它的下一個值。如果next方法被調用,但疊代器沒有值可以傳回,就會引發一個stopiteration異常。
使用疊代器的好處:
1)如果使用清單,計算值時會一次擷取所有值,那麼就會占用更多的記憶體。而疊代器則是一個接一個計算。
2)使代碼更通用、更簡單。
<b> 8.2.1 疊代器規則</b>
回憶下在python資料類型章節講解到字典疊代器方法,來舉例說明下疊代器規則:
>>> d = {'a':1, 'b':2, 'c':3}
>>> d.iteritems()
<dictionary-itemiterator object at 0x7f97c3b1bcb0>
# 判斷是否是疊代器
>>> from collections import iterator
>>> isinstance(d, iterator)
>>> isinstance(d.iteritems(), iterator)
# 使用next方法。
>>> iter_items = d.iteritems()
>>> iter_items.next()
('a', 1)
('c', 3)
('b', 2)
由于字典是無序的,是以顯示的是無序的,實際是按照順序擷取的下一個元素。
<b> 8.2.2 iter()函數</b>
使用iter()函數轉換成疊代器:
文法:
iter(collection) -> iterator
iter(callable, sentinel) -> iterator
>>> lst = [1, 2, 3]
>>> isinstance(lst, iterator)
>>> lst.next() # 不是疊代器是不具備next()屬性的
traceback (most recent call last):
file "<stdin>", line 1, in <module>
attributeerror: 'list' object has no attribute 'next'
>>> iter_lst = iter(lst)
>>> isinstance(iter_lst, iterator)
>>> iter_lst.next()
<b>8.2.3 itertools子產品</b>
itertools子產品是python内模組化塊,提供可操作疊代對象的函數。可以生成疊代器,也可以生成無限的序列疊代器。
有下面幾種生成無限序列的方法:
count([n]) --> n, n+1, n+2, ...
cycle(p) --> p0, p1, ... plast, p0, p1, ...
repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times
也有幾個操作疊代器的方法:
islice(seq, [start,] stop [, step]) --> elements from
chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...
groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v)
imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...
ifilter(pred, seq) --> elements of seq where pred(elem) is true
1)count生成序列疊代器
>>> from itertools import * # 導入所有方法
# 用法 count(start=0, step=1) --> count object
>>> counter = count()
>>> counter.next()
......
可以使用start參數設定開始值,step設定步長。
2)cycle用可疊代對象生成疊代器
# 用法 cycle(iterable) --> cycle object
>>> i = cycle(['a', 'b', 'c'])
>>> i.next()
'a'
'b'
'c'
3)repeat用對象生成疊代器
# 用法 repeat(object [,times]) -> create an iterator which returns the object,就是任意對象
>>> i = repeat(1)
......
可使用無限次。
也可以指定次數:
>>> i = repeat(1, 2)
stopiteration
4)islice用可疊代對象并設定結束位置
# 用法 islice(iterable, [start,] stop [, step]) --> islice object
>>> i = islice([1,2,3],2)
>>> i.next()
正常的話也可以擷取的3。
5)chain用多個可疊代對象生成疊代器
# 用法 chain(*iterables) --> chain object
>>> i = chain('a','b','c')
6)groupby将可疊代對象中重複的元素挑出來放到一個疊代器中
# 用法 groupby(iterable[, keyfunc]) -> create an iterator which returns
>>> for key,group in groupby('abcddcca'):
... print key,list(group)
a ['a']
b ['b']
c ['c']
d ['d', 'd']
groupby方法是區分大小寫的,如果想把大小寫的都放到一個疊代器中,可以定義函數處理下:
>>> for key,group in groupby('abcddcca', lambda c: c.upper()):
... print key, list(group)
c ['c', 'c']
7)imap用函數處理多個可疊代對象
# 用法 imap(func, *iterables) --> imap object
>>> a = imap(lambda x, y: x * y,[1,2,3],[4,5,6])
>>> a.next()
4
10
18
8)ifilter過濾序列
# 用法 ifilter(function or none, sequence) --> ifilter object
>>> i = ifilter(lambda x: x%2==0,[1,2,3,4,5])
>>> for i in i:
當使用for語句周遊疊代器時,步驟大緻這樣的,先調用疊代器對象的__iter__方法擷取疊代器對象,再調用對象的__next__()方法擷取下一個元素。最後引發stopiteration異常結束循環。
<b>8.3 生成器(generator)</b>
什麼是生成器?
1)任何包含yield語句的函數都稱為生成器。
2)生成器都是一個疊代器,但疊代器不一定是生成器。
<b>8.3.1 生成器函數</b>
在函數定義中使用yield語句就建立了一個生成器函數,而不是普通的函數。
當調用生成器函數時,每次執行到yield語句,生成器的狀态将被當機起來,并将結果傳回__next__調用者。當機意思是局部的狀态都會被儲存起來,包括局部變量綁定、指令指針。確定下一次調用時能從上一次的狀态繼續。
以生成斐波那契數列舉例說明yield使用:
斐波那契(fibonacci)數列是一個簡單的遞歸數列,任意一個數都可以由前兩個數相加得到。
#!/usr/bin/python
# -*- coding: utf-8 -*-
def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n += 1
fab(5)
# python test.py
5
使用yied語句,隻需要把print b改成yield b即可:
yield b
# print b
print fab(5)
<generator object fab at 0x7f2369495820>
可見,調用fab函數不會執行fab函數,而是直接傳回了一個生成器對象,上面說過生成器就是一個疊代器。那麼就可以通過next方法來傳回它下一個值。
>>> import test
>>> f = test.fab(5)
>>> f.next()
>>> f.next()
>>> f.next()
每次fab函數的next方法,就會執行fab函數,執行到yield b時,fab函數傳回一個值,下一次執行next方法時,代碼從yield b的吓一跳語句繼續執行,直到再遇到yield。
<b>8.3.2 生成器表達式</b>
在第四章 python運算符和流程控制章節講過,簡化for和if語句,使用小括号()傳回一個生成器,中括号[]生成一個清單。
回顧下:
# 生成器表達式
>>> result = (x for x in range(5))
>>> result
<generator object <genexpr> at 0x030a4fd0>
>>> type(result)
<type 'generator'>
# 清單解析表達式
>>> result = [ x for x in range(5)]
<type 'list'>
[0, 1, 2, 3, 4]
第一個就是生成器表達式,傳回的是一個生成器,就可以使用next方法,來擷取下一個元素:
>>> result.next()