迭代器
我們已經知道可以直接作用於 for 循環的數據類型有以下幾種:
- 一類是集合數據類型: list 、 tuple 、 dict 、 set 、 str 、 bytes 等。
- 另一類是 generator ,包括生成器和帶 yield 的 generator function。
這些可以直接作用於 for 循環的對象,統稱為可迭代的對象( Iterable ):
可迭代的對象,可以把它想成就是 可以循環的對象
, 可迭代 = 可循環
可以使用 isinstance()
判斷一個對象是否為 Iterable 對象
:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from collections import Iterable
print("[] list 列表是否為可迭代對象:", isinstance([], Iterable))
print("() tuple 元組是否為可迭代對象:", isinstance((), Iterable))
print("{} dict 字典是否為可迭代對象:", isinstance({}, Iterable))
print("( x for i in range(10) ) generator 生成器是否為可迭代對象:", isinstance( ( x for i in range(10) ), Iterable))
print("123 數字是否為可迭代對象:", isinstance(123, Iterable))
---------------執行結果---------------
[] list 列表是否為可迭代對象: True
() tuple 元組是否為可迭代對象: True
{} dict 字典是否為可迭代對象: True
( x for i in range(10) generator 生成器是否為可迭代對象: True
123 數字是否為可迭代對象: False
Process finished with exit code 0
而生成器不但可以作用於 for 循環,還可以被 __next__()
函數不斷地調用並返回下一個值,直到最後抛出 StopIteration 錯誤,表示無法繼續返回下一個值了。
注意,特重要 可以被 __next__() 函數調用,並不斷地返回下一個值的對象
稱為迭代器( Iterator )
可以使用 isinstance()
判斷一個對象是否為 Iterator 對象
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from collections import Iterator
print("[] list 列表是否為迭代器:", isinstance([], Iterator))
print("() tuple 元組是否為迭代器:", isinstance((), Iterator))
print("{} dict 字典是否為迭代器:", isinstance({}, Iterator))
print("( x for i in range(10) ) generator 生成器是否為迭代器:", isinstance( ( x for i in range(10) ), Iterator))
print("123 數字是否為迭代器:", isinstance(123, Iterator))
---------------執行結果---------------
[] list 列表是否為迭代器: False
() tuple 元組是否為迭代器: False
{} dict 字典是否為迭代器: False
( x for i in range(10) ) generator 生成器是否為迭代器: True
123 數字是否為迭代器: False
Process finished with exit code 0
生成器肯定是迭代器,因為有 __next__()
方法, 但迭代器一定是生成器嗎?答案是不一定
。
生成器都是 Iterator 對象,但 list、dict、str 雖然是 Iterable,卻不是 Iterator。
那如果想要把 list、dict、str等 Iterable 變成 Iterator的話,可以使用 iter()
函數:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from collections import Iterator
print("iter( [] ) list 列表是否為迭代器:", isinstance( iter([]), Iterator) )
print("iter( () ) tuple 元組是否為迭代器:", isinstance( iter(()), Iterator) )
print("iter( {} ) dict 字典是否為迭代器:", isinstance( iter({}), Iterator ) )
print("iter( ( x for i in range(10) ) ) generator 生成器是否為迭代器:", isinstance( iter(( x for i in range(10) ) ), Iterator ) )
print("iter( 123 ) 數字是否為迭代器:", isinstance(iter(123), Iterator))
---------------執行結果---------------
iter( [] ) list 列表是否為迭代器: True
iter( () ) tuple 元組是否為迭代器: True
iter( {} ) dict 字典是否為迭代器: True
iter( ( x for i in range(10) ) ) generator 生成器是否為迭代器: True
Traceback (most recent call last):
File "/Python/project/interator_pratice.py", line 16, in <module>
print("iter( 123 ) 數字是否為迭代器:", isinstance(iter(123), Iterator))
TypeError: ‘int‘ object is not iterable
Process finished with exit code 1
創建立一個普通的列表叫 a = [ 1, 2, 3, ]
, 然後使用 iter(a)
函數,這時候它就是一個迭代器了,然後在賦值給 b = iter(a)
,再來去用 __next__()
方法去調用它。
>>> from collections import Iterator
>>> a = [ 1, 2, 3 ]
>>> iter(a)
<list_iterator object at 0x10f91ee80>
>>> b = iter(a)
>>> b.__next__()
1
>>> b.__next__()
2
>>> b.__next__()
3
>>> b.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
你可能會問,為什麼 list、dict、str 等數據類型不是 Iterator ?
這是因為 Python 的 Iterator 對象表示的是一個數據流, Iterator 對象可以被 __next__()
函數調用,並不斷返回下一個數據,直到沒有數據時,拋出 StopIteration的錯誤
;可以把這個數據流看成是一個 有序序列
,但我們卻不能提前知道序列的長度,只能不斷地透過 __next__()
函數實現按需計算下一個數據,所以Iterator的計算是 惰性的
,只有在需要返回下一個數據時,它才會計算。
Iterator甚至可以表示一個無限大的數據流,例如:全體自然數。而使用 list 是永遠不可能存儲全體自然數的。
小結
凡是可以作用於for循環的對象都是 Iterable 類型
凡是可作用於 __next__() 函數的對象,都是 Iterator 類型,它們表示一個惰性計算的序列
集合數據類型,如: list 、 dict 、 str 等是 Iterable ,但不是 Iterator ,不過可以通過 iter() 函數獲得一個 Iterator 對象。
Python 的 for 循環本質上就是通過不斷調用 next() 函數實現的,例如:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
for i in [1, 2, 3, 4, 5]:
print(i)
實際上完全等於下面的代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# 先獲得 Iterator 對象
it = iter([1, 2, 3, 4, 5])
# 循環
while True:
try:
# 獲取下一個值
x = next(it)
# 把取到的值給打印出來
print(x)
# 遇到 StopIteration 就退出循環
except StopIteration:
break
透過上面代碼的解說,可以明白清楚地了解到 Python 3.x 的 range() 函數,其實本身就是迭代器,只是被封裝了起來。
Python 3.6.0 (default, Dec 24 2016, 00:01:50)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> range(0, 10)
range(0, 10)
>>>
但在 Python 2.7 的 range() 函數,就只是一個列表
Python 2.7.13 (default, Apr 4 2017, 08:46:44)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
那在 Python 2.7 中,想要把 range() 函數變成一個迭代器,只要使用 xrange() 函數,就可以把它變成一個迭代器了
Python 2.7.13 (default, Apr 4 2017, 08:46:44)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> xrange(0, 10)
xrange(10)
>>>
它真是一個迭代器嗎?來…讓我們來驗証
Python 2.7.13 (default, Apr 4 2017, 08:46:44)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> dir(xrange(0, 10))
[‘__class__‘, ‘__delattr__‘, ‘__doc__‘, ‘__format__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__hash__‘, ‘__init__‘, ‘__iter__‘, ‘__len__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__reversed__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘]
>>> i = xrange(0, 10)
>>> a = iter(i)
>>> dir(a)
[‘__class__‘, ‘__delattr__‘, ‘__doc__‘, ‘__format__‘, ‘__getattribute__‘, ‘__hash__‘, ‘__init__‘, ‘__iter__‘, ‘__length_hint__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘next‘]
>>> a.next()
0
...(略)
>>> a.next()
9
>>> a.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
好,現在大伙都知道什麼是 可迭代對象
跟 迭代器
,那接下來考考大家一個問題
a = [ 1, 2, 3 ]
- Q1: 上面代碼是
可迭代對象
嗎?
Ans: 是。詳解如下
>>> from collections import Iterable
>>> a = [ 1, 2, 3 ]
>>> print("a 是否為可迭代對象:", isinstance(a, Iterable) )
a 是否為可迭代對象: True
- Q2: 上面代碼是
迭代器
嗎?
Ans: 不是。因為 a
沒有 __next__
方法,所以不叫迭代器,我們透過 dir()
就可以查出 a 所有可以使用的方法有哪些,詳情如下。
>>> dir(a)
[‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__iadd__‘, ‘__imul__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__reversed__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__setitem__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘append‘, ‘clear‘, ‘copy‘, ‘count‘, ‘extend‘, ‘index‘, ‘insert‘, ‘pop‘, ‘remove‘, ‘reverse‘, ‘sort‘]
>>>