一、生成器函数进阶

1、最后一个yield后的代码

先看示例:

def generator():
print(123)
yield 'a'
print(456)
yield 'b'
print(789) g = generator()
print(next(g))
print("-------我是分割线-------")
print(next(g))
print("-------我是分割线-------")
print(next(g))
print("-------我是分割线-------")

得到的结果:

上述示例中,生成器函数generator中在最后一个yield后面还有代码,在取完最后一个值后再次执行next(),可以看到后面的“789”依然输出了,但是函数执行完还是找不到下一个yield,就报错了。

同样的,for循环取值也是可以将生成器执行完的,for循环也是以报错结束,只有在函数找不到最后一个yield的时候才会报错。

如果真的在返回最后一个值后面还需要有其他代码,可以在最后再添加一个yield 空。

2、send

生成器.send()与next()一样,可以获取生成器中的下一个值,不同的是,send可以向生成器内部传值。例:

 def generator():
content = yield 1 # 注意写法
print("生成器内部:", content)
yield 2 g = generator()
ret = next(g) # 生成器的第一个值必须用next获取
print("获取的第一个值:", ret)
ret = g.send("哈哈") # 使用send向内部传值
print("获取的第二个值:", ret)

结果:

可以看到,在生成器内部content成功接收到了外部传的值,而且同时,使用send也同样可以获取到生成器的下一个值。

send是在获取下一个值的时候,给上一个yield的位置传递一个数据

使用send的注意事项:

  1、第一次使用生成器的时候,只能使用next获取下一个值而不能使用send

  2、最后一个yield不能接收外部的值,因为后面已经没有代码了,所以传值也没有意义了

send其实就是实现了一个内外互传值的效果,因为每次yield相当于一次return,以yield分割,每部分就是一个函数,所以函数理应有接收参数的功能。

动态平均值

像射箭、打靶,都会有一个平均值的显示,用以标示当前选手已经打靶的平均环数,这个平均值是随着打靶的轮数每次重新计算的,现在用程序来写一个动态平均值的问题,就需要用到send方法了。

def dynamic_avg():
sum = 0
count = 0
avg = 0
while True:
new_value = yield avg # 传出当前平均值,接收新的值
sum += new_value
count += 1
avg = sum / count avg_g = dynamic_avg()
first = next(avg_g) # 过滤掉第一个值
while True:
value = input("请输入新的值:")
after = avg_g.send(int(value))
print("当前的平均值为:", after)

结果:

  上面的生成器已经实现了动态平均值的基本功能,但是存在一点不完美,第一个值并不是我们需要的,而且第一个值必须要使用next获取,我们希望如果这是我们写给用户的函数,用户能用最简单的方式去调用,比如只需要使用send就可以正常使用这个生成器。因此,我们可以对上面的函数进行改造,这就需要用到装饰器:

 def init(func):
"""
这是一个装饰器,对生成器函数进行初始化,过滤掉第一个值(预激协程的装饰器)
:param func: 生成器函数
:return: inner内部函数
"""
def inner(*args, **kwargs):
g = func()
next(g) # 在装饰器内部完成第一次调用
return g
return inner @init
def dynamic_avg():
sum = 0
count = 0
avg = 0
while True:
new_value = yield avg
sum += new_value
count += 1
avg = sum / count avg_g = dynamic_avg()
# first = next(avg_g) 不再需要使用next过滤掉第一个值
while True:
value = input("请输入新的值:")
after = avg_g.send(int(value))
print("当前的平均值为:", after)

拓展内容(模拟实现linux中的grep):

import os

def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper @init
def list_files(target):
while 1:
dir_to_search=yield
for top_dir,dir,files in os.walk(dir_to_search):
for file in files:
target.send(os.path.join(top_dir,file))
@init
def opener(target):
while 1:
file=yield
fn=open(file)
target.send((file,fn))
@init
def cat(target):
while 1:
file,fn=yield
for line in fn:
target.send((file,line)) @init
def grep(pattern,target):
while 1:
file,line=yield
if pattern in line:
target.send(file)
@init
def printer():
while 1:
file=yield
if file:
print(file) g=list_files(opener(cat(grep('python',printer())))) g.send('/test1') 协程应用:grep -rl /dir tail&grep

  这是一个层层递进式的查找文件内容的程序,装饰器实现了预激活,list_files获取文件夹下所有文件,然后将文件传给opener,opener打开文件然后将文件句柄传给cat,cat遍历文件内容,将文件的每行内容传给grep,grep进行与查找内容的比对,包含查找内容的就将文件名传给printer,printer进行打印,实现了一个利用生成器函数的完整的查找文件程序,各函数分工明确。当然,其实也可以不分这么多函数,这个更多的是深入了解生成器函数,同时也模仿了linux中的许多命令的功能。

3、yield from

yield from的中文讲解很少 , python官网是这样解释的

PEP 380 adds the yield from expression, allowing a generator to delegate part of its operations to another generator. This allows a section of code containing yield to be factored out and placed in another generator. Additionally, the subgenerator is allowed to return with a value, and the value is made available to the delegating generator.

官网链接

大意是说 yield from 表达式允许一个生成器代理另一个生成器, 这样就允许生成器被替换为另一个生成器, 子生成器允许返回值。例:

def generator():
s = 'abcdef'
yield from s # s返回的迭代器来代理这个生成器 g = generator()
print(next(g))
print(next(g))
print(next(g))

二、列表推导式

[表达式 for 变量 in 列表]  或者  [表达式 for 变量 in 列表 if 条件]

第一种写法举例: [x for x in range(5)] 、 [x*x for x in range(5)] 、 ["鸡蛋%s"%x for x in range(5)] 、 [(i, j) for i, j in enumerate(range(5), 10)] 、

[i for i in enumerate(range(5), 10)] 、 [(x, y) for x in range(10) for y in range(5)]

第二种写法举例: [x for x in range(10) if x % 2 == 0] ,其他表达式写法同第一种。

所以列表表达式前面的表达式可以任意写,后面的其实就是循环体加条件。

三、生成器表达式

生成器表达式与列表表达式的写法完全一致,只是括号为小括号“()”,即 (表达式 for 变量 in 列表)  或者  (表达式 for 变量 in 列表 if 条件)

注意:小括号不能省略

例:

# 打印10以内的偶数的平方
g = (x**2 for x in range(10) if x % 2 == 0)
for i in g:
print(i)

结果:

生成器表达式与列表推导式的区别:

1、括号不一样

2、返回的值不一样,生成器表达式不管结果多大,都占用很小的内存,而列表推导式还是将所有的值一次性取出,占用内存会随结果的大小改变。

四、其他推导式

1、字典推导式

例一:将一个字典的key和value对调

mcase = {'a': 10, 'b': 34}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)

例二:合并大小写对应的value值,将k统一成小写

mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
print(mcase_frequency)

2、集合推导式

例:计算列表中每个值的平方,自带去重功能

squared = {x**2 for x in [1, -1, 2]}
print(squared)
# Output: set([1, 4])

五、练习题

例1:过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母

例2:求(x, y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表

例3:求M中3, 6, 9组成的列表M=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

其中第二题,题目原意是

[(x, y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1]

但因为自己一开始理解错误,以为是有一个列表,从中取出符合条件的元祖列表,写成了:

lst = [(1, 4), (3, 0), (2, 5), (1, 9), (3, 4), (8, 3)]
[i for i in lst if 0 <= i[0] <= 5 and 0 <= i[1] <=5 and i[0] % 2 ==0 and i[1] % 2 == 1]

  这个结果倒是与我想的一致,但是pycharm在中间代码 0 <= i[0] <= 5 and 0 <= i[1] <=5 and i[0] % 2 ==0 一直提示我可以简化,但是我一直看不出哪里可以简化,就让pycharm自动简化了,结果变成了 5 >= i[0] >= 0 == i[0] % 2 and 0 <= i[1] <= 5 ,这个写法至少在我所知的语言里是没有的,之前只知道可以把大于小于写在一起,不知道可以一直联等下去,发现python真的是将语言高度精简了,上面这个问题也可以改成 5 >= i[0] >= 0 <= i[1] <= 5 and i[0] % 2 == 0 ,感觉又发现了新大陆。

Python学习 day14的更多相关文章

  1. python学习 day14 (3月19日)----

    04 json # 1. 用于多种语言 交互 编程语言通用数据 # 内置的 不需要安装直接导入使用 import json # 导入 # # dumps loads dump load # dic = ...

  2. python学习-day14:集合,函数,格式化

    一.集合 定义:由不同元素组成的集合.集合是一组无序排列的可hash值, 可以作为字典的key.元素必须是不可变类型:只能存放数字,字符串,字典 特性:集合的目的是将不同的值放在一起,不同的集合之间可 ...

  3. python学习day14 装饰器(二)&模块

    装饰器(二)&模块 #普通装饰器基本格式 def wrapper(func): def inner(): pass return func() return inner def func(): ...

  4. python学习Day14 带参装饰器、可迭代对象、迭代器对象、for 迭代器工作原理、枚举对象、生成器

    复习 函数的嵌套定义:在函数内部定义另一个函数 闭包:被嵌套的函数 -- 1.外层通过形参给内层函数传参 -- 2.返回内部函数对象---->  延迟执行, 开放封闭原则: 功能可以拓展,但源代 ...

  5. python学习博客地址集合。。。

    python学习博客地址集合...   老师讲课博客目录 http://www.bootcdn.cn/bootstrap/  bootstrap cdn在线地址 http://www.cnblogs. ...

  6. 【目录】Python学习笔记

    目录:Python学习笔记 目标:坚持每天学习,每周一篇博文 1. Python学习笔记 - day1 - 概述及安装 2.Python学习笔记 - day2 - PyCharm的基本使用 3.Pyt ...

  7. python学习之旅

    python学习分类 python基础    +- day01——python初始.变量.常量.注释.基础数据类型.输入.if day02——while.字符串格式化.运算符.编码初识 day03—— ...

  8. Python学习--04条件控制与循环结构

    Python学习--04条件控制与循环结构 条件控制 在Python程序中,用if语句实现条件控制. 语法格式: if <条件判断1>: <执行1> elif <条件判断 ...

  9. Python学习--01入门

    Python学习--01入门 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.和PHP一样,它是后端开发语言. 如果有C语言.PHP语言.JAVA语言等其中一种语言的基础,学习Py ...

随机推荐

  1. eclipse 市场

    http://marketplace.eclipse.org/ 如何找工具官网? 举例:UMLet 进入官网以后,下载插件,然后离线安装.

  2. 策略和计费控制(PCC)系统研究

    策略和计费控制(PCC)系统研究 研究内容 [TOC "float:left"] 策略与计费控制(PCC)框架1 [架构图](achitecture.png "Archi ...

  3. spark介绍3

  4. 配置hive环境以及mysql配置后必须做

    1.先在主节点上安装阿里云配置(看别的文档) 2.把需要的两个jar包加入进来(放到hadoop用户目录下面即可即/home/hadoop/) mysql-connector-java-5.1.47. ...

  5. RPM管理,计划任务与性能监控

    =========== RPM 软件包管理: 相关命令: rpm -ivh 软件包名称 rpm -e 软件包名称     rpm -U 软件名称 软件包信息查询: 相关命令: rpm -q 查询指定软 ...

  6. Java50道经典习题-程序1 不死神兔

    题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少? 分析:假如:1月1日有1对兔子,根据题目“从出生后 ...

  7. 【项目总结】扯一扯电商网站前端css的整体架构设计(1)

    最近半忙不忙的写了一个外包网站,网站主要功能是艺术品竞拍和艺术衍生品的销售.工程已经完成了80%左右,现在前后端代码量已经50W行左右,我主要负责的是前端设计和前端布局.下面就先放一个网站的设计图吧, ...

  8. cisco和h3c网络设备中一次性打印全部配置信息

    cisco的是全页打印配置信息的命令: #terminal length 0 #show run 华为和h3c的是: >screen-length 0 temporary >display ...

  9. leetcode 39 组合总和 JAVA

    题目: 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无限制 ...

  10. STR[#6]

    photo 小明在旅游的路上看到了一条美丽的河,河上有许多船只,有的船只向左航行,有的船只向右航行.小明希望拍下这一美丽的风景,并且把尽可能多的船只都完整地拍到一张照片中. 小明位于河的边上,并且可以 ...