先看一个栗子:

# -*- 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)

使用单线程去切换任务

1,线程是由操作系统切换的,单线程切换一位置我们需要自己去调度任务

2,不在需要锁,并发性高,如果单线程内切换函数,性能将远高于线程切换,并发性更高

传统函数调用 过程 A->B->C
我们需要一个可以暂停的函数,并且可以在适当的时候恢复该函数的继续执行
出现了协程 -> 有多个入口的函数, 可以暂停的函数, 可以暂停的函数(可以向暂停的地方传入值)
def gen_func():
#1. 可以产出值, 2. 可以接收值(调用方传递进来的值)
html = yield "http://projectsedu.com"
print(html)
return "bobby" #1. throw, close #1. 生成器不只可以产出值,还可以接收值 if __name__ == "__main__":
gen = gen_func()
#在调用send发送非none值之前,我们必须启动一次生成器, 方式有两种1. gen.send(None), 2. next(gen)
url = gen.send(None)
#download url
html = "天青色等烟雨"
print(gen.send(html)) #send方法可以传递值进入生成器内部,同时还可以重启生成器执行到下一个yield位置
print(gen.send(html))
#1.启动生成器方式有两种, next(), send

我们需要的是传入一个网址之后返回的内容在原封不动的传回来

在调用send之后也会执行下一个yield的结果,并且开始的时候必须使用next()或者使用send()一个空值

调用gen.close()生成器已经结束了

gen = gen_func()
print(next(gen))
gen.throw(Exception, "download error")
print(next(gen))
gen.throw(Exception, "download error")

这三个操作就可以模拟协程,可以暂停,关闭,发送异常

yield from语法

在解释之前先了解一下

from itertools import chain

chain函数可以将多个可迭代对象进行一个for循环

my_list = [1,2,3]
my_dict = {
"web1":"http://projectsedu.com",
"web2":"http://www.imooc.com",
}
for value in chain(my_list,my_dict,range(10)):
print(value)

yield from 后面加的是一个可迭代的对象

yield from会自动将对象迭代出来

def g1(iterable):
yield iterable def g2(iterable):
yield from iterable for value in g1(range(10)):
print(value)
for value in g2(range(10)):
print(value)
range(10)
>>>
0
1
2
3
4
5
6
7
8
9

可以看出这两个的区别

def g1(gen):
yield from gen def main():
g = g1()
g.send(None) #1. main 调用方 g1(委托生成器) gen 子生成器
#1. yield from会在调用方与子生成器之间建立一个双向通道

具体用法:

final_result = {}

def sales_sum(pro_name):
total = 0
nums = []
while True:
x = yield
print(pro_name+"销量: ", x)
if not x:
break
total += x
nums.append(x)
return total, nums def middle(key):
while True:
final_result[key] = yield from sales_sum(key)
print(key+"销量统计完成!!.") def main():
data_sets = {
"面膜": [1200, 1500, 3000],
"手机": [28,55,98,108 ],
"大衣": [280,560,778,70],
}
for key, data_set in data_sets.items():
print("start key:", key)
m = middle(key)
m.send(None) # 预激middle协程
for value in data_set:
m.send(value) # 给协程传递每一组的值
m.send(None)
print("final_result:", final_result)
#
if __name__ == '__main__':
main()
"""
看完代码,我们总结一下关键点: 1. 子生成器生产的值,都是直接传给调用方的;调用方通过.send()发送的值都是直接传递给子生成器的;如果发送的是 None,会调用子生成器的__next__()方法,如果不是 None,会调用子生成器的.send()方法;
2. 子生成器退出的时候,最后的return EXPR,会触发一个StopIteration(EXPR)异常;
3. yield from表达式的值,是子生成器终止时,传递给StopIteration异常的第一个参数;
4. 如果调用的时候出现StopIteration异常,委托生成器会恢复运行,同时其他的异常会向上 "冒泡";
5. 传入委托生成器的异常里,除了GeneratorExit之外,其他的所有异常全部传递给子生成器的.throw()方法;如果调用.throw()的时候出现了StopIteration异常,那么就恢复委托生成器的运行,其他的异常全部向上 "冒泡";
6. 如果在委托生成器上调用.close()或传入GeneratorExit异常,会调用子生成器的.close()方法,没有的话就不调用。如果在调用.close()的时候抛出了异常,那么就向上 "冒泡",否则的话委托生成器会抛出GeneratorExit异常。 """

生成器函数yield的更多相关文章

  1. Python 3 中生成器函数yield表达式的使用

    生成器函数或生成器方法中包含了一个yield表达式.调用生成器函数时,会返回一个迭代子,值从迭代子中每次提取一个(通过调用其__next__()方法).每次调用__next__()时,生成器函数的yi ...

  2. ES6学习笔记<三> 生成器函数与yield

    为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...

  3. 生成器函数_yield_yield from_send

    生成器函数 ''' yield类似于return 相同: 都返回出去一个值 不同: yield每次返回时, 会记录当前执行的位置 等下次调用生成器, 会从该位置向下走 return 直接终止函数 '' ...

  4. day12-Python运维开发基础(推导式、生成器与生成器函数)

    1. 推导式(列表推导式.集合推导式.字典推导式) # ### 推导式 : 通过一行循环判断,遍历出一系列数据的方式是推导式 """ 推导式一共三种: 列表推导式,集合推 ...

  5. 020.Python生成器和生成器函数

    一 生成器 1.1 基本概念 元组推导式是是生成器(generator) 生成器定义 生成器可以实现自定义,迭代器是系统内置的,不能够更改 生成器的本质就是迭代器,只不过可以自定义. 生成器有两种定义 ...

  6. yield生成器函数

    生成器有主要有四种方法: next() 执行函数,直到遇到下一个yield为止,并返回值 send(value) 为生成器发送一个数值,next()方法就相当于send(None) close() 终 ...

  7. 巨蟒python全栈开发-第12天 生成器函数 各种推导式 yield from

    一.今日主要内容总览(重点) 1.生成器(目的:帮助我们创建对象) (1)生成器的本质就是迭代器 (2)一个一个的创建对象 (3)创建生成器的方式: 1.生成器函数 2.通过生成器表达式来获取生成器 ...

  8. ES6笔记(5)-- Generator生成器函数

    系列文章 -- ES6笔记系列 接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术. 在异步编程中,还 ...

  9. python迭代器和生成器(3元运算,列表生成式,生成器表达式,生成器函数)

    1.1迭代器 什么是迭代器: 迭代器是一个可以记住遍历的位置对象 迭代器对象从集合的第一个元素元素开始访问,直到所有元素被访问完结束,迭代器只能往前不会后退. 迭代器有两个基本方法:iter ,nex ...

随机推荐

  1. 获取cpu和内存使用情况

    public class SystemInfo { [DllImport("kernel32")] public static extern void GetSystemDirec ...

  2. Galaxy2D Game Engine 4.2

    Galaxy2D Game Engine 4.2 开发版  下载地址  D3DRender注意:1.下载后的压缩包请使用WinRar5.0打开.2.开发版包含了现在正在开发中的Galaxy2D游戏引擎 ...

  3. docker开启远程访问

    作者:zqifa 出处:http://www.cnblogs.com/zqifa/ 默认情况下,Docker守护进程Unix socket(/var/run/docker.sock)来进行本地进程通信 ...

  4. css修改整个项目的滚动条样式

    在项目中,滚动条不可避免的药出现.设置统一规范的滚动条也是必然.用一个独立的css文件即可修改整个项目中的滚动条样式 . scrollBar.css: /* 滚动条有滑块的轨道部分 */ ::-web ...

  5. mvc开发中DTO,DO,FROM的区别

    DO:数据库实体类映射到model里的实体类,每个字段都和数据库相对应,一般来说开发的时候不要去添加或者修改里面的实体 DTO:与前台交互的时候(一般来说是查询操作)有一些数据字段是那一张表里面没有囊 ...

  6. python类特列方法使用

    class Rgc(object): def __new__(cls, *args, **kwargs): print('在类通过__new__方法实例化一个对象') return super(Rgc ...

  7. 当map遇到parseInt

    也是一道面试题,估计除了面试题,一般情况下,也不会写出类似的代码了. ['1', '2', '3'].map(parseInt) 这么一道题的返回结果是什么? 如果不用浏览器去验证,乍一看,似乎确实没 ...

  8. Linux java进程无故被kill

    这两天,演示环境的java应用无端端就被停止了.在这里记录一下原因和排查过程: 发现应用挂掉以后,第一时间登陆centos,发现进程没有了.于是重新启动应用. 但是启动不起来,去看业务日志,没有找到任 ...

  9. 在虚拟机上运行zookeeper的过程中,xshell连接不上虚拟机

    之后网上的各种方法,都没用 如图,就是第一个网卡配置文件.下面又具体的详解.网上有说让把Onboot改成yes,而我的本来就是yes,就没该, 之后我对比了我原来电脑你虚拟机的网卡配置文件,我发现on ...

  10. 盘点一下Github上开源的Java面试/学习相关的仓库,看完弄懂薪资至少增加10k

    最近浏览 Github ,收藏了一些还算不错的 Java面试/学习相关的仓库,分享给大家,希望对你有帮助.我暂且按照目前的 Star 数量来排序. 本文由 SnailClimb 整理,如需转载请联系作 ...