---------------------------------------------------------------前言------------------------------------------------------------------------------
- iterable被認為是一類對象,這類對象能夠一次返回它的一個成員(也就是元素)。是適合迭代的對象。
- 實際上,任何具有__iter__()或__getitem__()方法的對象,Python就認為它是一個iterable。
- Python大量內置的iterable類型,如: list,str,tuple,dict,file,xrange等。使用內置的iter()函數來生成iterator。iter(iterable) -> iterator object
- Sequence的字面意思是序列。既然是序列,那么就應該有成員,成員之間是有序的且包含了若干成員。
- sequence首先是iterable。如果這個iterable可以通過整數索引來訪問其元素並可以獲
得其大小,那么它是sequence。 - iterator對象就是且必須實現了迭代協議(iterator protocol【1】【2】)的對象。Python里的iterator實現了兩個方法:
__iter__() # 返回迭代器本身
__next__() # Python2使用next()
- iterable: 至少定義了__iter__()或__getitem__()方法的對象。
- sequence: 至少定義了__len__()或者__getitem__()方法的對象。
- iterator:至少定義__iter__()和__next__()法的對象。
--------------------------------------------------------------正文------------------------------------------------------------------------------
想必Python的老鳥們對於Python里的iterable,sequence和iterators應該非常了解,且能做到運用自如。
但是對於剛剛開始學習Python的新手們來說,這三個玩意兒估計夠讓他們抓狂的。先看一段有趣的代碼:
-
>>> l = ['a', 'b', 'c']
-
>>> l_iter = l.__iter__()
-
>>> l
-
['a', 'b', 'c']
-
>>> l_iter
-
<list_iterator object at 0x7f40f2c46668>
-
>>> for e in l: print(e)
-
...
-
a
-
b
c
-
>>> for e in l: print(e)
-
...
-
a
-
b
-
c
-
>>> l_iter.__next__()
-
'a'
-
>>> l_iter.__next__()
-
'b'
-
>>> for e in l_iter: print(e)
-
...
-
c
-
>>> l_iter.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
-
>>> list(l_iter)
-
[]
從這段代碼,可以看出一些特點:
list和由list生成的list_iterator都可以進行遍歷。
list可以無限次的使用,而list_iterator只能使用一次。
list_iterator通過__next__()方法來獲取下一個元素,且只能獲取一次。
當最后一個元素被獲取后,如果想繼續,StopIteration將會拋出。
其實在這里,list就是iterable(也是sequence),而l_iter則是iterator,為什么它們會有那些特點呢?下面將
詳細介紹它們及其之間的關系。
-
Iterable
在Python里iterable被認為是一類對象,這類對象能夠一次返回它的一個成員(也就是元素)。抽象一點就是
適合迭代的對象。實際上,任何具有__iter__()或__getitem__()方法的對象,Python就認為它是一個iterable。
Python里有大量內置的iterable類型,如: list,str,tuple,dict,file,xrange等。使用內置的iter()函數來生成
iterator。即:
iter(iterable) -> iterator object
-
Sequence
Sequence的字面意思是序列。既然是序列,那么就應該有成員,成員之間是有序的且包含了若干成員。當然
從理論上講,序列的成員數可以是無限制的。那么將其抽象成數據類型后,便可定義為有序對象的集合。維基
百科是這樣定義sequence的:
A sequence is an ordered list. Like a set, it contains members (also called elements, or terms).
The number of ordered elements (possibly infinite) is called the length of the sequence.
從定義可以看出sequence有三個特性:
包含成員(元素)
有序成員(有序)
成員數量(大小)
Python里這樣定義sequence:
An iterable which supports efficient element access using integer indices via the __getitem__()
special method and defines a __len__() method that returns the length of the sequence.
從定義里可以看出,sequence首先是iterable。如果這個iterable可以通過整數索引來訪問其元素並可以獲
得其大小,那么它是sequence。很顯然,dict是iterable,但不是sequence,即使它有__getitem__() 和 __len__()
這兩種方法。因為它不是通過整數索引而是通過key來獲取元素的。Python將sequence對象的三個特性變成了兩
個方法__getitem__()和 __len__()。通過這兩個方法我們可以獲得sequence對象的元素和大小。
-
Iterator
Iterator(迭代器)存在於眾多面向對象的程序設計語言中,它是一種經典的設計模式。迭代器模式提供一種訪問
有序訪問聚合對象里元素的方法。具體到Python語言里,正如在我的前一篇博文《Iterators詳解》介紹的,iterator
對象就是且必須實現了迭代協議(iterator protocol【1】【2】)的對象。Python里的iterator實現了兩個方法:
__iter__() # 返回迭代器本身
__next__() # Python2使用next()
通過iterable, sequence和iterator的定義可以看出這三者的密切關系。下圖很好的詮釋了它們之間的關系。
很顯然,Python認為sequence和iterator都是iterable。當然還有一部分non-sequence也被認為是iterable。例如,
dictionary,set等。定義了__getitem__()和__len__()方法的non-sequence對象和sequence對象都可以用iter()來
返回一個iterator。iter(X)是這樣工作的(X是一個iterable):
調用X的__iter__()方法來生成一個iterator。
如果X沒有__iter__()方法,Python將會自動構建一個iterator,並嘗試通過X[0], X[1], X[2] ... 或X[key1],x[key2] ... 來獲取元素。
Iterable里,sequence和non-sequence的區別就在於訪問元素的方式,通過整數索引(index)訪問的是seqence,
而不能通過整數索引訪問的是non-sequence。其實,我們完全可以通過對象定義的方法來區分一個對象到底是iterable,
sequence還是iterator。
iterable: 至少定義了__iter__()或__getitem__()方法的對象。
sequence: 至少定義了__len__()或者__getitem__()方法的對象。
iterator:至少定義__iter__()和__next__()法的對象。
下面來看一些實例:
-
#!/usr/bin/env python
-
# -*- coding: utf -*-
-
class Train(object):
-
-
def __init__(self, cars):
-
self.cars = cars
-
def __len__(self):
-
return self.cars
-
def __getitem__(self, key):
-
if key >= 0:
-
index = key
else:
-
index = self.cars + key
-
if 0 <= index < len(self):
-
return 'Carriage #%s' % (index + 1)
else:
-
raise IndexError('No carriage at #%s' % key)
這里,我們定義了一個名為Train的類實現了一個火車的sequence。這個實現了sequence的兩個關鍵的方法:
__len__()和__getitem__(),實際上這兩個方法被叫作sequence protocol。實現了這兩個方法,也就實現了一
個immutable 【4】的seqence。
-
>>> from train import Train
-
>>> t = Train(4)
-
>>> t
-
<train.Train object at 0x7f491e457080>
>>> t[0]
-
'Carriage #1'
-
>>> len(t)
4
-
>>> t.__len__()
-
4
-
>>> t.__getitem__(2)
-
'Carriage #3'
-
>>> t[3]
-
'Carriage #4'
-
>>> t[-3]
'Carriage #2' -
>>> for i in t:
... print(i)
...
Carriage #1
Carriage #2
Carriage #3
Carriage #4
從上面的實驗代碼可以看出,我們可以對 t 進行遍歷,獲取元素和大小。毫無疑問,這個t對象有__getitem__()
方法,所以它也是iterable。我們可以將用t來生成新的list,tuple等對象,也可生成iterator對象。請看下面的代碼:
-
>>> list(t)
-
['Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4'] # 生成了一個list對象
-
>>> iter(t)
-
<iterator object at 0x7f491e4755f8>
-
>>> tuple(t)
-
('Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4') # 生成了一個tuple對象
-
>>> t_iter = iter(t)
>>> t_iter
<iterator object at 0x7f491e475da0> # 生成了一個iterator對象
>>> "__iter__" and "__next__" in dir(t_iter) #
True
>>>
那么iterator和iterable(非iterator的iterable)到底有什么區別呢?我先看下面的代碼:
-
>>> t
-
<train.Train object at 0x7f491e457080>
-
>>> t[0]
-
'Carriage #1'
-
>>> t[3]
-
'Carriage #4'
-
>>> t[4]
-
Traceback (most recent call last):
-
File "<stdin>", line 1, in <module>
-
File "/home/pygeek/DEV/pyprj/reading/train.py", line 22, in __getitem__
-
raise IndexError('No carriage at #%s' % key)
-
IndexError: No carriage at #4
-
>>> t[2]
-
'Carriage #3'
-
>>> t[1]
-
'Carriage #2'
-
>>> for i in t: # 第一次遍歷
-
... print(i)
-
...
-
Carriage #1
-
Carriage #2
-
Carriage #3
-
Carriage #4
-
>>> for i in t: # 第二次遍歷
... print(i)
...
Carriage #1
Carriage #2
Carriage #3
Carriage #4
t 對象是sequence,當然它也是iterable,但是它不是iterator。我們通過索引或遍歷訪問元素。當索引超出t對象
的長度范圍時,IndexError將會被拋出。t對象可以被多次甚至無限次的使用。但是,iterator確實有些不一樣了。
-
>>> t_iter = iter(t)
-
>>> t_iter
-
<iterator object at 0x7f491c9ac4e0>
-
>>> t_iter[0]
Traceback (most recent call last):
-
File "<stdin>", line 1, in <module>
-
TypeError: 'iterator' object is not subscriptable
-
>>> t_iter.__next__()
-
'Carriage #1'
-
>>> t_iter.__next__()
-
'Carriage #2'
-
>>> t_iter.__next__()
-
'Carriage #3'
-
>>> t_iter.__next__()
-
'Carriage #4'
-
>>> t_iter.__next__()
-
Traceback (most recent call last):
-
File "<stdin>", line 1, in <module>
-
StopIteration
-
>>> for i in t_iter:
-
... print(i)
-
...
-
>>>
上面的代碼說明,iterator不能通過索引來訪問元素,但是可以用它的__next__()方法來訪問元素;可進行遍歷,
但是只能完整遍歷一次。當所有的元素都被訪問過后,再調用__next__(),拋出的異常是StopIteration,而不是
IndexError。此時,我們稱iterator已經被耗盡,不能再進行遍歷。實際上,iterator存儲了元素的位置狀態並可以
通過調用__next__()訪問下一個元素並更新內部的位置狀態直到所有的元素都被訪問。這些不同使得iterator對象
會比list,tuple這樣的iterable更有效率並占用更少的內存空間。我們可以用sys.getsizeof()來做一粗略的測試:
-
>>> t_iter = iter(t)
-
>>> t_iter
-
<iterator object at 0x7f491e479780>
-
>>> t_list = list(t)
-
>>> t_list
-
['Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4']
-
>>> t_tuple = tuple(t)
-
>>> t_tuple
-
('Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4')
-
>>> sys.getsizeof(t), sys.getsizeof(t_iter), sys.getsizeof(t_list), sys.getsizeof(t_tuple)
-
(56, 56, 120, 80)
我們可以看到list對象比iterator對象的兩倍還要大。所以,如果我們僅僅只是需要iterable的元素,而不需要一個
完整的list的話,那么用iterator將更有效率並節省空間。
-------------------------------------------------------------------后記------------------------------------------------------------------
簡單總結一下:
iterable: 實現了__iter__()或__getitem__()方法的對象。
sequence:實現是了sequence protocol(即方法: __getitem__()和__len__()),並能使用整數索引訪問元素的iterable對象。
iterator: 實現了iterator protocol(即方法:__next__()和__iter__())的iterable對象。
通過iterable創建iterator的方法:
iter(iterable) -> iterator
iterable.__iter__() -> iterator 【5】
-
注:
【1】所有的代碼都在Python 3.4.0下測試通過。
【2】protocol: a synonym for interface used in dynamic languages like Python,Ruby,...
【3】iterator protocol: The iterator protocol consists of two methods. The __iter__() method, which must return the iterator object and the __next__() method, which returns the next element.
【4】immutable: An object with a fixed value. Such an object cannot be altered. A new object has to be created if a different value has to be stored.
【5】iterable.__iter__()返回的是一個新的iterator,而iterator.__iter__()返回的iterator對象本身。