玩轉可迭代對象迭代器生成器


玩轉可迭代對象迭代器生成器


在Python中,經常可以看到可迭代對象、迭代器、生成器,如何得到一個可迭代對象,如何把它變成迭代器,如何得到生成器,它們到底有什麼區別和聯繫呢?

簡單來說,它們的關係如下圖

從概念上來說,可迭代對象 > 迭代器 > 生成器。

可迭代對象

可迭代對象Iterable,可以認為是一個容器,其中有N個元素,可以迭代。

在Python中可以簡單的認為,能夠使用for循環遍歷的,都是可迭代對象。常見的類型由list、tuple、range對象、str、bytes、bytearra、set、dict等。


自定義可迭代對象

自定義類型,如何變成一個可迭代對象?

<code>class MyIterable:
def __str__(self):
return "我還不是一個可迭代對象"

mi = MyIterable()

print(mi)

for i in mi:
print(i) # 拋異常'MyIterable' object is not iterable/<code>

實現__iter__魔術方法

<code>class MyIterable:
def __iter__(self):
print('iter~~~~~~~')

mi = MyIterable()
print(mi)

for i in mi:
print(i) # 拋異常iter() returned non-iterator of type 'NoneType'/<code>

可以看到實例mi確實是可迭代對象了,但是返回值是None,所以報錯了,看異常提示,__iter__返回一個迭代器才是正確的。

迭代器

迭代器Iterator

  • 迭代器是一種特殊可迭代對象,一定能迭代
  • 可以使用內建函數next()來獲取它的下一個元素
  • 使用next迭代完所有元素後,如果繼續獲取下一個元素,則拋出StopIteration異常
  • 使用next迭代完所有元素後,不可再次迭代

判斷是否是迭代器,其實如果可以使用next函數來獲取元素的對象,一定是迭代器。

那麼,列表是迭代器嗎?

<code>a = [1, 2]
print(next(a)) # 'list' object is not an iterator/<code>

列表對象是可迭代對象,但不是迭代器。如何把一個可迭代對象轉成迭代器呢?

  1. 使用內建函數iter
  2. 包裝成生成器對象
<code>a = [1, 2]
# print(next(a)) # 'list' object is not an iterator
b = iter(a) # 內建函數iter

print(next(b))
print(next(b))
print(next(b)) # StopIteration/<code>

很多函數都能返回迭代器,例如enumerate

<code>a = [1, 2]
c = enumerate(a) # 返回迭代器
print(next(c))
print(next(c))
for x in c: # 已經迭代完了,沒有什麼元素可以迭代了
print(x)
print('=' * 30)

print(next(c)) # StopIteration/<code>

自定義迭代器對象

那麼,上面MyIterable的例子可以修改如下

<code>class MyIterable:
def __init__(self):
self.items = [1,2,3,4,5]

def __iter__(self):
print('iter~~~~~~~')
return iter(self.items)

mi = MyIterable()
print(mi)

for i in mi: # mi可以迭代了
print(i)

for i in mi: # 可迭代對象可再次迭代
print(i)

print(next(mi)) # 不可以使用next,說明mi不是迭代器/<code>

mi對象成為了可迭代對象,但是它不是迭代器。

如何得到迭代器呢?使用__next__魔術方法

<code>class MyIterable:
def __init__(self):
self.items = [1,2,3,4,5]
self.count = 0

def __iter__(self):
print('iter~~~~~~~')
return iter(self.items)

def __next__(self):
print('next ~~~~')
try:
count = self.count
n = self.items[count]
self.count = count + 1
return n
except IndexError:
raise StopIteration


mi = MyIterable()

print(mi)
print(next(mi))
print(next(mi))
print(next(mi))
print(next(mi))
print(next(mi))
print(next(mi)) # StopIteration/<code>

代碼再改進一下

<code>class MyIterable:
def __init__(self):
self.items = [1,2,3,4,5]
self.count = 0

def __iter__(self):
print('iter~~~~~~~')
#return iter(self.items)
return self # 有__iter__我就是可迭代對象,因為有__next__,我自己就是迭代器

def __next__(self):
print('next ~~~~')
try:
count = self.count
n = self.items[count]
self.count = count + 1
except:
raise StopIteration
return n


mi = MyIterable()
print(mi)
print(next(mi)) # 迭代器

for i in mi: # mi也是可迭代對象
print(i)
print('-' * 30)

for i in mi: # 已經不能再次迭代
print(i)

print('-' * 30)
print(next(mi)) # StopIteration/<code>

生成器

Python中使用2種方式獲得生成器對象

  • 生成器表達式
  • 生成器函數

生成器是特殊的迭代器,但迭代器不一定是生成器。它是Python提供的通過編程方式快速便捷得到迭代器的手段

<code>g1 = (i for i in range(5)) # 生成器表達式
print(g1) # 生成器對象
print(next(g1))
print(next(g1))
print('=' * 30)


def counter(): # 生成器函數
for i in range(5, 10): # 可以使用yield from
yield i

g2 = counter() # 生成器函數調用不返回結果,返回一個生成器對象
print(g2)
print(next(g2))
print(next(g2))/<code>

總結

Python 3開始,推薦在能使用迭代器的地方,考慮使用迭代器,它是惰性計算對象,不會短時間對內存和CPU造成大的壓力。

可以使用生成器表達式非常便利得到一個迭代器,稍微複雜一些可以考慮使用生成器函數獲得一個迭代器,如果更加複雜可以使用類來構造。

【年薪30w工程師吐血整理資料大合集】

領取IT資料大合集:http://image.qbangmang.com/counselor.html

玩轉可迭代對象迭代器生成器


分享到:


相關文章: