python 生成器&迭代器

自動變數發表於2019-02-21

 

一、生成器

1、列表生成器:列表生成式就像是一個廚師,他只會做這n(n為任意整數)道菜,想吃甚麼做甚麼,不吃不做,不浪費空間;而列表表示式就相當於已經做好的n盤菜,佔用空間。
2、生成器的建立方法:

  • s = (x*2 for x in range(5))
  • yield+數字     只要有yield就是一個生成器物件

注意:生成器在建立的時候已經決定了能計算出值得個數,呼叫next的個數一旦超過這個值就會報錯

   第一種:

s = (x*2 for x in range(5))

# 生成器的呼叫方法:next()
print(next(s)) # 提取生成器裡面的值,in python2中等價於 s._next_(),next() 一次列印一次
print(next(s))
print(next(s)) # 生成器就是一個可迭代物件(Iterable) for i in s: # 遍歷生成器可迭代物件 print(i) # 列印完一個數之後這個數就被回收,不佔用記憶體,只佔用被引用值的哪一個記憶體

>>> 0  2  4  6  8

執行機制:從0開始,第一次next()時,取出一個數*2輸出,然後每next()一次,取一個數,一直到2,for迴圈遍歷時,因為前面next()已經取到了數字2的位置,所以從3開始,故輸出為0 2 4 6 8

   第二種:

def fun():         # 注意:生成器物件是fun(),而不是fun
    print(`s`)
    yield 1

    print(`ss`)
    yield 2

g = fun()
print(g)           # <generator object fun at 0x000001D54E12A0F8>
 
next(g)            # 有返回值,為yield後的值
next(g)

解釋:程式執行時先執行g = fun(), 執行到第一個next()時,返回到函式中執行具體內容,一直到執行完yield 1 停止,然後繼續下一個next()再到函式中尋找yield 2 的內容,以此類推注意:如果再多一個next(g)就會報錯:StopIteration(停止迭代),因為超過了迭代次數

什麼是可迭代物件:內部有 _iter_() 的都是可迭代物件(列表、元組、字典)

def fun():   
    print(`s`)
    yield 1

    print(`ss`)
    yield 2

for i in fun():
    print(i)       # yield 有返回值,返回值賦給了i

>>>

s
1
ss
2

例1:輸出斐波那契數列

def fib(maxs):
    n, before, after = 0, 0, 1
    while n < maxs:
        print(after)       # 從1開始,如列印before則從從0開始
        before, after = after, before+after      # 先計算後賦值(先右後左)
        n = n+1

fib(10)     # 輸出前10個斐波那契數

注意:before, after = after, before+after  和 下面的兩條語句不等價,原因見例1對應標註;

before = after
after =  before+after

例2:斐波那契數列生成器

def fib(maxs):
    n, before, after = 0, 0, 1
    while n < maxs:
        yield before
        before, after = after, before+after      # 先計算後賦值(先右後左)
        n = n+1

g = fib(8)
print(g)     # 生成器所在的記憶體地址

# 需要幾個next() 幾次,但最多不能超過8次,否則應修改fib() 裡面的數字
print(next(g))
print(next(g))
print(next(g))
print(next(g))

3、 .send() 方法

def fun():        # fun()是一個生成器物件而不是fun
    print(`qwer`)
    count = yield 1  # 先把yield的值返回,然後把‘xss’賦值給變數count
    print(count)

    print(`asdf`)
    yield 2

f = fun()
# next(f)      # 進入函式體
f.send(None)   # 等價於next(f)  
s = f.send(`xss`)   # 給yield前面的變數賦值
print(s)    

# s 是yield 2 的返回值

  注意:第一次send前如果沒有next,只能傳一個send(None),否則報錯,TypeError: can`t send

non-None value to a just-started generator,第一次通過進入send(None) 進入函式之後首先執行

print(),yield返回一個1,再次進入之後就把send()裡面的值傳給count,然後就可以對count進行操作

例3:yield 偽併發的實現

import time

def consumer(name):
    print("%s準備吃包子!" % name)
    while True:
        baozi = yield
        print("包子[%s]來了,被[%s]吃了!" % (baozi, name))

def producer(name):
    a1 = consumer(`A`)
    a2 = consumer(`B`)
    next(a1)
    next(a2)
    print("%s準備吃包子了!" % name)
    for i in range(5):
        time.sleep(1)
        print("做了2個包子")
        a1.send(i)
        a2.send(i)

producer("xiaoss")

>>>

      A準備吃包子!
      B準備吃包子!
      xiaoss準備吃包子了!
      做了2個包子
      包子[0]來了,被[A]吃了!
      包子[0]來了,被[B]吃了!
      做了2個包子
      包子[1]來了,被[A]吃了!
      包子[1]來了,被[B]吃了!
      做了2個包子
      包子[2]來了,被[A]吃了!
      包子[2]來了,被[B]吃了!
      做了2個包子
      包子[3]來了,被[A]吃了!
      包子[3]來了,被[B]吃了!
      做了2個包子
      包子[4]來了,被[A]吃了!
      包子[4]來了,被[B]吃了!

 

二、迭代器

1、什麼是迭代器?

    迭代是指可以用for迴圈來遍歷

     for迴圈內部三件事: 1.呼叫可跌代物件的iter方法返回一個可迭代物件
                                        2.不斷呼叫可迭代物件的next方法
                                        3.處理StopIteration

  處理StopIteration的過程如下:

 

while:
    try:
        I = next(list_Iterator)
    
    except StopIteration:
        break

  注:生成器都是迭代器,迭代器不一定是生成器

2、滿足兩個條件: (1)有iter方法  (2) 有next方法   

from collections import Iterable,Iterator   # 呼叫Iterable和Iterator

lis = [1, 2, 3, 4]
d = iter(lis)   # 相當於 l._iter_()
print(d)        # <list_iterator object at 0x00000206700DC358>
print(next(d))
print(next(d))
print(next(d))
print(next(d))

for i in [1, 2, 3, 4]:
    iter([1, 2, 3, 4])

print(isinstance([1, 2], list))    # 判斷一個字元是不是某個特定的資料型別 isinstance(資料, 資料型別)
print(isinstance(lis, Iterable))   # 判斷lis是不是迭代器

>>>

<list_iterator object at 0x000002756B321240>
1
2
3
4
True
True

例1:用迭代找出文件中最長的行

F = max(len(x.strip()) for x in open(`file_1`, `r`))      # 檔案讀取,找出最長的行
print(F)       # 最長行字元的個數

相關文章