1. 程式人生 > >生成器函式yield

生成器函式yield

先看一個栗子:

# -*- coding:UTF-8 -*-
__autor__ = 'zhouli'
__date__ = '2018/12/6 21:08'


# 生成器函式,函式裡只要有yield關鍵字

def gen_func():
    yield 1


def func():
    return 1


if __name__ == "__main__":
    gen = gen_func()
    re = func()
    pass

生成器函式這個物件是是什麼時候產生的呢?是python編譯位元組碼的時候就產生了,

既然是生成器物件,那麼一定可以使用for迴圈進行遍歷,並且yield可以多次

def gen_func():
    yield 1
    yield 2
    yield 3
    yield 4

yield的特性:惰性求值, 延遲求值提供了可能

斐波那契數列的經典舉例:

def fib(index):
    if index <= 2:
        return 1
    else:
        return fib(index-1) + fib(index-2)
print(fib(10))

這樣雖然可以做出來,但是沒有具體的過程,那改進一下

def fib2(index):
    relist = []
    n,a,b 
= 0,0,1 while n<index: relist.append(b) a, b = b, a+b n += 1 return relist

假如說現在index很大,上億,那記憶體就有可能不夠了。

def fib2(index):
    n,a,b = 0,0,1
    while n<index:
        yield b
        a, b = b, a+b
        n += 1

改成這樣,內部沒有維護一個列表,自然而然就不會消耗記憶體的

當然這樣可以直接進行for迴圈了

那生成器的原理是什麼呢?適用於什麼場景呢?如何區別於函式呢?

def foo():
    bar()


def bar():
    global frame
    frame = inspect.currentframe()


# python.exe會用一個叫做 PyEval_EvalFramEx(c函式)去執行foo函式, 首先會建立一個棧幀(stack frame)
python一切皆物件,棧幀物件, 位元組碼物件
當foo呼叫子函式 bar, 又會建立一個棧幀
所有的棧幀都是分配在堆記憶體上,這就決定了棧幀可以獨立於呼叫者存在

利用生成器表示式讀取大檔案:

有人可能講了,for line in f.open()

但是如果只有一行呢?

f.read(4096)  # 先讀4096個字元
f.read(4096)  # 自動再次讀取4096個字元
# 500G, 特殊 一行
def myreadlines(f, newline):
    buf = ""
    while True:
        while newline in buf:
            pos = buf.index(newline)
            yield buf[:pos]
            buf = buf[pos + len(newline):]
        chunk = f.read(4096)

        if not chunk:
            # 說明已經讀到了檔案結尾
            yield buf
            break
        buf += chunk


with open("input.txt") as f:
    for line in myreadlines(f, "{|}"):
        print(line)