Python詳解可迭代對象(Iterable)、序列(Sequence)、迭代器(Iterator)、生成器(generator)

一篇文章徹底瞭解 可迭代對象(Iterable)、序列(Sequence)、迭代器(Iterator)、生成器(generator)。

閱讀本文不知道需要幾分鐘,但你真的能徹底弄懂這幾個概念

可迭代對象(Iterable)

網上看了很多關於這幾個概念的解釋,看得很暈,最後還是直接看官方文檔,最靠譜。這裏也將直接用官方文檔中的定義來說話。

關於 Iterable,文檔是這樣定義的:

iterable
An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an iter() method or with a getitem() method that implements Sequence semantics.

Iterables can be used in a for loop and in many other places where a sequence is needed (zip(), map(), …). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call iter() or deal with iterator objects yourself. The for statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also iterator, sequence, and generator.

如果你嫌太長,可以不看。提練重點如下:

1、它是能夠一次返回一個成員的對象,也就是可以 for…in 遍歷的;

2、所有的序列類型(也就是後面要說到的 Sequence),都是可迭代對象,如 list、str、tuple,還有映射類型 dict、文件對象等非序列類型也是可迭代對象

3、自定義對象在實現了 iter() 方法或者實現了 getitem() 方法後,也可以成爲可迭代對象;

4、iter()方法接受一個可迭代對象,該方法的返回值是一個迭代器(Iterator)

簡單示例如下:

from collections import Iterable, Iterator
# 定義一個 list
l = [1, 2, 3]
print(isinstance(l, Iterable))  # True,列表是可迭代對象,同樣 tuple、str、dict 也是的

# 遍歷
for i in l:
    print(i)  # 每次遍歷返回一個元素

# 自定義可迭代對象示例見下面注意事項

li = iter(l)
print(type(li))  # list_iterator
print(isinstance(li, Iterator))  # True

那麼如何判斷一個對象是可迭代對象呢?很容易想到的方法是 isinstance,這時候我們需要注意一點,文檔介紹如下:

class collections.abc.Iterable
ABC for classes that provide the iter() method.

Checking isinstance(obj, Iterable) detects classes that are registered as Iterable or that have an iter() method, but it does not detect classes that iterate with the getitem() method. The only reliable way to determine whether an object is iterable is to call iter(obj).

簡單解釋就是:通過 isinstance(obj, Iterable) 判斷一個對象是否是可迭代對象時,只有當這個對象被註冊爲 Iterable 或者當它實現了 iter() 方法的時候,才返回 True,而對於實現了 getitem() 方法的,返回的是 False。所以穩當判斷是否是可迭代對象的方式是調用 iter(obj),如果不報錯,說明是可迭代對象,反之則不是。

示例代碼如下:

"""
驗證 Iterable
"""
# 導包
from collections import deque, Iterable, Iterator

# 定義一個啥都沒有的類
class MyIterable1:
    pass

# 定義一個只實現了 __getitem__ 的類
class MyIterable2:
    def __init__(self, *args):
        self._list = deque()
        self._list.extend(args)

    def __getitem__(self, index):
        return self._list.popleft()

# 定義了一個只實現了 __iter__ 的類
class MyIterable3:
    def __init__(self, *args):
        self._list = deque()
        self._list.extend(args)

    def __iter__(self):
        return iter(self._list)

# mi = MyIterable1()
# iter(mi)  # 報錯: TypeError: 'MyIterable1' object is not iterable

mi2 = MyIterable2(1, 2, 3)
# 可循環
for i in mi2:
    print(i)  # 1, 2, 3
print(isinstance(mi2, Iterable))  # False
iter(mi2)  # 不報錯

mi3 = MyIterable3(1, 2, 3)
# 同樣可遍歷
for i in mi3:
    print(i)
iter(mi3)  # 同樣不報錯
print(isinstance(mi3, Iterable))  # 而這裏返回是 True

序列(Sequence)

關於 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. Some built-in sequence types are list, str, tuple, and bytes. Note that dict also supports getitem() and len(), but is considered a mapping rather than a sequence because the lookups use arbitrary immutable keys rather than integers.

The collections.abc.Sequence abstract base class defines a much richer interface that goes beyond just getitem() and len(), adding count(), index(), contains(), and reversed(). Types that implement this expanded interface can be registered explicitly using register().

提練重點如下:

1、可迭代;

2、支持下標訪問,即實現了 getitem() 方法,同時定義了 len() 方法,可通過 len() 方法獲取長度;

3、內置的序列類型:list、str、tuple、bytes;

4、dict 同樣支持 getitem() 和 len(),但它不歸屬於序列類型,它是映射類型,因爲它不能根據下標查找,只能根據 key 來查找;

5、抽象類 collections.abc.Sequence 還提供了很多方法,比如 count()、index()、contains()、reversed()可用於擴展;

總結結論:序列一定是一個可迭代對象,但可迭代對象不一定是序列

迭代器(Iterator)

上文檔:

An object representing a stream of data. Repeated calls to the iterator’s next() method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its next() method just raise StopIteration again. Iterators are required to have an iter() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a list) produces a fresh new iterator each time you pass it to the iter() function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.

提練重點:

1、一個表示數據流的對象,可通過重複調用 next(或使用內置函數next())方法來獲取元素。當沒有元素存在時,拋出 StopIteration 異常;

2、iter(obj)接受一個迭代器作爲參數時,返回的是它本身。在可迭代對象裏我們說過,iter(obj)方法不報錯,說明它一定是一個可迭代對象。因此迭代器一定是一個可迭代對象;

3、一個迭代器必須要實現 iter() 方法。但因爲迭代器前提必須是一個可迭代對象,所以只實現 iter() 方法不一定是一個迭代器。

示例代碼如下:

from collections import Iterable, Iterator, deque


print("="*30)

# 定義一個可迭代對象
l = [1, 2, 3]

# 轉換成迭代器,只需要調用 iter(obj) 方法
li = iter(l)
print(isinstance(li, Iterator))  # True

# 可重複調用 next() 方法獲取其中的一個元素
print(next(li))  # 1
print(next(li))  # 2
print(next(li))  # 3

# 每次獲取完一個元素,這個元素就會被“消費”
# 當元素獲取完畢後,報 StopIteration 異常
# next(li)  # StopIteration


print("=========iterator1=========")
# 當一個自定義類,只實現 __next__ 方法時
# 它不是一個可迭代對象,因此,它也不是一個迭代器
# 但是它可以調用 next() 方法獲取值
class MyIterator1:
    def __init__(self, *args):
        self._list = deque()
        self._list.extend(args)

    def __next__(self):
        return self._list.popleft()

m1 = MyIterator1(1, 2, 3)
print(next(m1))
print(next(m1))
print(next(m1))

# print(next(m1))  # IndexError
print(isinstance(m1, Iterator))  # False


print("=========iterator2=========")


class MyIterator2:
    def __init__(self, *args):
        self._list = deque()
        self._list.extend(args)

    def __iter__(self):
        return iter(self._list)

    def __next__(self):
        return self._list.popleft()


m2 = MyIterator2(1, 2, 3)
print(next(m2))
print(next(m2))
print(next(m2))

# print(next(m2))  # IndexError
print(isinstance(m2, Iterator))  # True


print("=========iterator3=========")


class MyIterator3:
    def __init__(self, *args):
        self._list = deque()
        self._list.extend(args)

    def __getitem__(self):
        return iter(self._list)

    def __next__(self):
        return self._list.popleft()


m3 = MyIterator3(1, 2, 3)
print(next(m3))
print(next(m3))
print(next(m3))

# print(next(m3))  # IndexError
print(isinstance(m3, Iterator))  # False

總結結論:

1、迭代器一定是可迭代對象,但可迭代對象不一定是迭代器;

2、可迭代對象可以通過直接調用 iter(obj) 方法轉換成迭代器;

3、自定義對象轉換迭代器,必須實現 iter 和 next 。同時實現 getitem 和 next 也是可以達到 next() 訪問值的效果,但是通過 isinstance 判斷返回 False,這裏返回 False,應該和可迭代器判斷返回 False 的原因是一致的;

4、迭代器每次調用 next() 能拿到一下值,但它是一次性消費的,當獲取使用過後,無法再拿到原來的值。

生成器(generator)

文檔解釋如下:

A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.

提練重點:

1、它是一個迭代器

2、它是一個含有特殊關鍵字 yield 迭代器

3、每次生成一個值,可通過 next() 方法獲取。

實際上生成器的實現有兩種方式,一種是通過 yield 關鍵字,另外一種是通過生成器表達式,示例代碼如下:

import types

# yield 方式
def my_generator():
    for i in range(3):
        yield i

g = my_generator()
print(type(g))  # <class 'generator'>
isinstance(g, types.GeneratorType)  # True
next(g)  # 0
next(g)  # 1
next(g)  # 2
next(g)  # exception StopIteration

# 使用生成器表達式
l = [i for i in range(3)]  # 列表推導式
g = (i for i in range(3))  # 將 [] 換成 () 即是生成器表達式
isinstance(g, types.GeneratorType)  # True

總結

1、迭代的方式有兩種,一種是通過下標,即實現 getitem,一種是直接獲取值,即實現 iter,兩種方式都可通過 for…in 方式進行遍歷。也都是可迭代對象;

2、isinstance 判斷可迭代對象時,針對下標訪問的判斷有出入,需要特別注意;

3、可迭代對象基本要求是可遍歷獲取值;

4、序列一定是可迭代對象,它實現了 len()  getitem,可獲取長度,可通過下標訪問;

5、迭代器一定是可迭代對象,它實現了 next()

6、生成器是特殊的迭代器,它一定是迭代器,因此也一定是可迭代對象。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章