1. 程式人生 > 其它 >python-迭代器生成器

python-迭代器生成器

python-迭代器生成器

目錄

python 三大利器, 迭代器,生成器和裝飾器

迭代是Python中常用且非常強大的一個功能,可以被for迴圈的就是可迭代的,目前有字串,列表,元組,字典,集合。 通過使用兩個單獨方法來實現的;它們被用於允許使用者自定義類對迭代的支援。

可迭代物件iterable 是Python中一個非常龐大的概念,它主要包括如下三類

  • 迭代器 (生成器)

  • 序列(字串、列表、元組)

  • 字典

  • 可迭代物件需要實現__iter__方法

  • 迭代器不僅要實現__iter__方法,還需要實現__next__方法

  • 生成器是一個返回迭代器的函式,使用了yield,

迭代器是可迭代物件的一個子集,而生成器又是迭代器的一個子集,是一種特殊的迭代器。除了迭代器之外,Python中還有序列、字典等可迭代物件。

可迭代物件

可迭代協議: 能被for迴圈的就是“可迭代的”, 可迭代協議的定義:內部實現了__iter__方法。

iterator.__iter__()

alist=[1,2]
print(alist.__iter__())
#<list_iterator object at 0x1024784a8>

返回迭代器物件本身。 如 range(),str,list,tuple,dict,set

迭代器(iterator)

l = [1,2,3]
l_iter = l.__iter__()
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
#
1 
2
3
StopIteration

迭代器遵循迭代器協議:必須擁有__iter__方法和__next__方法。

iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set)

迭代器是可迭代物件的一個子集,它是一個可以記住遍歷的位置的物件,它與列表、元組、集合、字串這些可迭代物件的區別就在於next方法的實現,其他列表、元組、集合、字串這些可迭代物件可以很簡單的轉化成迭代器,通過Python內建的iter函式能夠輕鬆把可迭代物件轉化為迭代器

構造迭代器iter(),next()

# 內建函式**iter()** 函式用來生成迭代器

iter(object[, sentinel])
'''輸入引數
object   -- 支援迭代的集合物件, 可迭代物件
sentinel -- 如果傳遞了第二個引數,則引數 object 必須是一個可呼叫的物件
'''
x_list = [1,2,3,4,5]
y_list=iter(x_List)
print(type(x_List))     # <class 'list'>
print(type(y_list))     # <class 'list_iterator'>

print(y_list)
print(next(y_list))    # <list_iterator object at 0x0000016A17F231F0>
print(next(y_list))    #  1 
print(next(y_list))    #  2

# 通過iter函式把list轉化成了迭代器
# 迭代器能夠記住遍歷位置,能夠通過next方法不斷從前往後訪問


# 自定義類

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
     if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration    # StopIteration 異常用於標識迭代的完成
 
myclass = MyNumbers()
myiter = iter(myclass)
 
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))

迭代器有兩種:一種是呼叫方法直接返回的,一種是可迭代物件通過執行iter方法得到的,迭代器有的好處是可以節省記憶體。

生成器

生成器是迭代器的子集,換句話說,生成器一定是迭代器,但是迭代器不全是生成器物件。

  • 生成器函式:常規函式定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函式的狀態,以便下次重它離開的地方繼續執行
  • 生成器表示式:類似於列表推導,但是,生成器返回按需產生結果的一個物件
  • 本質:迭代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)
  • 特點:惰性運算,開發者自定義

在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator,生成器是一個返回迭代器的函式, 生成器是一個特殊的程式,可以被用作控制迴圈的迭代行為,使用yield返回值函式,每次呼叫yield會暫停,而可以使用next()函式和send()函式恢復生成器。

Python中的關鍵字yiled,在Python中一個函式可以用yiled替代return返回值,這樣的話這個函式就變成了一個生成器物件

def generator(array):
    for i in array:
        return i
    
gen = generator([1,2,3,4,5])
print(type(gen))
# 輸出
<class 'int'>
def fun_generator(array):
    for i in array:
        yield(i)
        
gen = fun_generator([1,2,3,4,5])
print(type(gen))
​
# 輸出  呼叫一個生成器函式,返回的是一個迭代器物件。
<class 'generator'>   
# 例項使用 yield 實現斐波那契數列

def fibonacci(n): # 生成器函式 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一個迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

列表推導式和生成器表示式

X = [1, 2, 3, 4, 5]
it = [i for i in X]
gen = (i for i in X)
print(type(X))
print(type(it))
print(type(gen))

# 輸出
<class 'list'>
<class 'list'>
<class 'generator'>

# 使用中括號的叫做列表生成式, 惰性計算,延遲求值
# 獲得的返回值是一個列表,而使用小括號叫做生成器表示式,獲得的返回結果是一個生成器
gen = (i for i in X)
for i in gen:
    print(i)
# 輸出
1
2
3
4
5

應用特點

對於大資料量處理,將會非常有用。

#列表解析
sum([i for i in range(100000000)])	#記憶體佔用大,機器容易卡死
 
#生成器表示式
sum(i for i in range(100000000))	#幾乎不佔記憶體

迭代器在編碼方面更加簡潔,這是顯而易見的,其次生成器執行速度更快,最後一點,也是需要著重說明的一點:節省記憶體.

4G 記憶體處理 10G 大小的檔案

# 方法1
# 僅使用 Python 內建模板,逐行讀取到記憶體。
# 使用 yield,好處是解耦讀取操作和處理操作:

def python_read(filename):
    with open(filename,'r',encoding='utf-8') as f:
        while True:
            line = f.readline()
            if not line:
                return
            yield line
            
if __name__ == '__main__':
    g = python_read('./data/movies.dat')
    for c in g:
        print(c)
        # process c

方法一有缺點,逐行讀入,頻繁的 IO 操作拖累處理效率。

# 方法2
# 使用pandas  包 read_csv 函式,引數有 38 個之多,功能非常強大
def pandas_read(filename,sep=',',chunksize=5):
    reader = pd.read_csv(filename,sep,chunksize=chunksize)
    while True:
        try:
            yield reader.get_chunk()
        except StopIteration:
            print('---Done---')
            break
使用如同方法一:

if __name__ == '__main__':
    g = pandas_read('./data/movies.dat',sep="::")
    for c in g:
        print(c)
        # process c