先看一个栗子:
# -*- 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)