1. 程式人生 > >Python 函數與常用模組 - 叠代器

Python 函數與常用模組 - 叠代器

doc 集合 process cal (()) bin back lis ()

叠代器

我們已經知道可以直接作用於 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‘]
>>>

Python 函數與常用模組 - 叠代器