《python-美藏篇》1.可迭代、迭代器與生成器

365/24/60發表於2022-01-01

首先區分可迭代物件(Iterable)迭代器(Iterator)

  • 可迭代物件:包含__iter__方法的物件為可迭代物件,如List、Tuple、Dictionary
  • 迭代器:包含__iter____next__方法

可迭代物件屬於半成品 --> 而迭代器屬於直接食用產品,生成器屬於特殊的迭代器,生成器函式執行後會返回生成器

以上型別判斷方法

from collections import Iterator,Iterable
from inspect import isgenerator , isgeneratorfunction 
print(isgenerator(g),isinstance(g ,Iterator))
print(isgenerator(it1) , isgenerator(iter(it1)),isinstance(it1,Iterator))

以下均在for 迴圈遍歷中即是根據 iter 方法判斷該物件是否是可迭代序列

1. 定義一個可迭代類:

class Iterat():
    def __init__(self):
        self.arr = [1,2,3]
        self.loc = 0
    def __iter__(self):
         for i in self.arr:
             yield i
for i in Iterat():
  print(i)
# 進行for迴圈遍歷時,先執行iter方法迭代化,返回一個迭代器物件,迭代器物件含有next方法,接下來通過next遍歷

上述定義中,可能出現的報錯Error

  1. 類class定義不含__iter__方法(不能識別為可迭代序列)
  2. 類class中iter方法既沒有使用 yield 也沒有通過 `return self (簡單說就是iter方法執行後沒有返回迭代器物件)

2. 定義一個標準的迭代器:

2.1 類class生成法:

class Iterat():
    def __init__(self):
        self.arr = [1,2,3]
        self.loc = 0
    def __iter__(self):
        return self    # 返回自身,這裡再新建也可,殊途同歸
    def next(self):
    #  python 2.7 中寫法: next 
        if self.loc < len(self.arr):
            ret = self.arr[self.loc]
            self.loc += 1
            return ret
        else:
            raise StopIteration
    def __next__(self):
    # python 3 以上寫法
        if self.loc < len(self.arr):
            self.loc += 1
            return self.arr[self.loc]
        else:
            raise StopIteration

2.2 yield 生成器法:原理:如果函式中存在yield,該函式將被作為生成器,執行後自動生成一個迭代器,且無需管理StopIteration邊界問題

for i in Iterat():執行找iter方法,執行返回了迭代器(編譯器自動構造,self不是原有的類),此時class中的next方法不會被執行
class Iterat():
    def __init__(self):
        self.arr = [1,2,3]
    def __iter__(self):
        for i in self.arr:
          yield i  

生成器(Generator)是一種特殊的迭代器,是一種優化使用方式的直接使用產品(無需管理邊界問題,負責吃就完事了)

生成器生成函式(GeneratorFunction)不是生成器,是一種工廠函式

gt = ( i for i in range(3))  # 生成器構造語法糖
# 下面例子是一個生成器建構函式,凡是函式有yield,會被作為生成器建構函式處理
def genGT(list):
  for item in list :
    yield item

最後做一個小實驗比較下迭代器(流式讀取)與普通讀取的差異:(犧牲時間換空間,可以使用shell命令觀察記憶體、cpu變化)

it = ( i for i in range(100000000))
count = 0 
s = time.time()
for i in it :
    count += i
print(time.time()-s)  # 31.8320000172 s 

list = [ i for i in range(100000000)]
count = 0 
s = time.time()
for i in list :
    count += i
print(time.time()-s)  # 27.4390001297 s

相關文章