python学习笔记:第12天 列表推导式和生成器
1. 迭代器
什么是生成器呢,其实生成器的本质就是迭代器;在python中有3中方式来获取生成器(这里主要介绍前面2种)
- 通过生成器函数获取
- 通过各种推导式来实现生成器
生成器函数
我们来看一个普通的函数:
In[2]: def func1():
...: print('aaaa')
...: return 1111
...:
In[3]: fun = func1()
aaaa
In[4]: print(fun)
1111
那么生成器函数跟普通函数有什么不同呢,我们只要把其中的return换成yield关键字参数就是生成器函数了:
In[5]: def func1():
...: print('aaaa')
...: yield 1111
...:
In[6]: fun = func1() # 此时并没有任何打印信息,可以说明函数并没有执行
In[7]: print(fun) # 从输出可以看出这是一个生成器对象
<generator object func1 at 0x0000016F900D6DB0>
从上面的结果来看,我们发现函数func1根本就没有执行,而最后打印的是一个内存地址,这个就是生成器很明显的一个特性:惰性计算,那么我们要怎么执行它呢?我们可以回顾一下迭代器的取值方法:使用迭代器的__next__的方法可以取到迭代器的一个值,那生成器的本质就是迭代器,那我们也可以试下可以这样取值
In[8]: fun.__next__() # 从输出可以看出,yield也和return一样可以有返回值
aaaa # 这里我们就可以看到函数中的aaaa也打印了,表示函数在此处才执行
Out[8]: 1111
我们再来看个例子,观察下生成器是怎么工作的:
In[9]: def func1():
...: print('aaaa')
...: yield '我是第一个yield'
...: print('bbbb')
...: yield '我是第二个yield'
...: print('cccc')
...:
In[10]: gen = func1() # 这里得到的是一个生成器,此处并不会运行函数
...: print(gen)
<generator object func1 at 0x0000016F900F8BA0>
In[11]: print(gen.__next__()) # 首次执行生成器的__netx__()函数时,开始执行函数,
aaaa # 直到遇到yield时返回,并且yield也可以有返回值
我是第一个yield
In[12]: print(gen.__next__()) # 再次运行__netx__()函数时,会继续执行函数(从上次yield的位置继续执行)
bbbb
我是第二个yield
In[13]: print(gen.__next__()) # 再次执行__next__()方法继续执行,此处再往下执行时没有了yield关键字,
cccc # 会抛出StopIteration异常(但时会执行后面的代码)
Traceback (most recent call last):
File "D:\Environment\python-virtualenv\jupyter\lib\site-packages\IPython\core\interactiveshell.py", line 3265, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-13-9340d28f24b7>", line 1, in <module>
print(gen.__next__())
StopIteration
从上面我们呢可以总结出:
- yield也可以像return一样也是返回值
- yield执行完之后会返回到调用者,执行后续的代码,直到再次调用__next__方法,此时生成器函数再从上次停止的位置继续执行
- 当执行
__next__方法后没有yield关键字时,会抛出StopIteration异常,但是会执行yield后面的代码
send方法
接下来我们来看send⽅法, send和__next__()⼀样都可以让⽣成器执⾏到下⼀个yield
In[14]: def eat():
...: print("aaaa")
...: a = yield 1111
...: print("a=",a)
...: b = yield "bbbb"
...: print("b=",b)
...: c = yield "cccc"
...: print("c=",c)
...: yield "GAME OVER"
...:
In[15]: gen = eat() # 获取⽣成器
In[16]: ret1 = gen.__next__()
...: print(ret1)
aaaa
1111
In[17]: ret2 = gen.send("我send了一个参数给a")
...: print(ret2)
a= 我send了一个参数给a # 可以看出send的数据是被上一个yield前的a给接收了
bbbb
In[18]: ret3 = gen.send("我send了一个参数给b")
...: print(ret3) # 这里send的数据也是被b接收了
b= 我send了一个参数给b
cccc
In[19]: ret4 = gen.send("我send了一个参数给c")
...: print(ret4)
c= 我send了一个参数给c
GAME OVER
send和__next__():
- send和next()都是让⽣成器向下走⼀次
- send可以给上⼀个yield的位置传递值, 不能给最后⼀个yield发送值. 在第⼀次执⾏⽣成器代码的时候不能使⽤send()
2. 推导式
列表推导式
关于列表推导式,其实之前的文章中已经使用过,这里再正式介绍下;假设我们要打印1到20之间的奇数,照之前正常的写法我们要这么写:
# 假设有一个需求,要写一个循环遍历1到20之间所有的奇数
lst = []
for i in range(1, 21):
if i % 2 == 1:
lst.append(i)
print(lst)
# 结果:
# [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
列表推导式的语法为:
- 第一种只使用for循环遍历
[expr for item in itratorable]
# 相当于以下代码
ret = []
for item in iterable:
ret.append(expr)
- 第二种for循环遍历再加if条件判断
[expr for item in iterable if cond]
# 相当于以下结构代码
ret = []
for item in iterable:
if cond:
ret.append(expr)
第三种for循环加if双分支结构,注意此时的if/else语句要写在for语句前面
[expr1 if cond else expr2 for item in iterable ]
# 相当于以下代码
ret = []
for item in iterable:
if cond:
ret.append(expr1)
else:
ret.append(expr2)
对于上面的例子使用列表推导式可以这样写:
# 使用推导式:
lst = [i for i in range(1, 21) if i % 2 == 1]
print(lst)
# 结果:
# [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
使用列表推导式我们可以发现代码时精简了许多,而且代码的可读性更高了,其实还有一个优势是推导式速度更快:
In [1]: %%timeit
...: lst1 = []
...: for i in range(10000):
...: lst1.append(i)
...:
788 µs ± 14.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [2]: %%timeit
...: lst1 = [i for i in range(10000)]
...:
307 µs ± 1.84 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [3]:
从上面的结果分析,使用列表推导式生成列表的方式要比普通for循环的效率要高很多
字典推导式
字典跟列表推导式的语法非常相似,使用{}括起来,然后在里面想列表推导式一样写自己的表达式即可:
dic = {expr for k, v in iterable if cond} # 这里的expr表达式可以写成:k: v的形式
# 相当于以下代码
dic = dict()
for k, v in iterable:
if cond:
expr(dic)
例如,把字典中的键值对都调换以下可以用如下方法:
dic = {"张无忌":"赵敏", "杨过":"小龙女", "郭靖":"黄蓉"}
# dic = {'k1':'v1', 'k2': 'v2', 'k3': 'v3'}
dic = {v: k for k, v in dic.items()}
print(dic)
生成器表达式
对于生成器表达式来说,只需要把列表推导式的中括号换成小括号就可以了:
In[20]: def inc(x):
...: print('inc {0}'.format(x))
...: return x+1
...:
In[21]: g = (inc(x) for x in range(10)) # 这里的g就是一个生成器对象
In[22]: print(g)
<generator object <genexpr> at 0x0000016F90161DB0>
In[23]: print(g.__next__())
inc 0
1
In[24]: print(g.__next__()) # 也可以使用__next__方法取出一个值
inc 1
2
In[25]: print(g.__next__())
inc 2
3
In[26]: next(g) # 使用netx()和__next__()方法是一样的
inc 3
Out[26]: 4
In[27]: next(g)
inc 4
Out[27]: 5
当然,生成器表达式也可以跟其他推导式一样套用if语句,其语法都是一样的,这里就不做介绍了。
⽣成器表达式和列表推导式的区别:
列表推导式比较耗内存. ⼀次性加载. ⽣成器表达式⼏乎不占⽤内存. 使⽤的时候才分
配和使⽤内存得到的值不⼀样. 列表推导式得到的是⼀个列表. ⽣成器表达式获取的是⼀个⽣成器.
⽣成器的惰性机制: ⽣成器只有在访问的时候才取值. 说⽩了. 你找他要他才给你值. 不找他
要. 他是不会执⾏的.
def func():
print(111)
yield 222
g = func() # ⽣成器g
g1 = (i for i in g) # ⽣成器g1. 但是g1的数据来源于g
g2 = (i for i in g1) # ⽣成器g2. 来源g1
print(list(g)) # 获取g中的数据. 这时func()才会被执⾏. 打印111.获取到222. g完毕.
print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了. g1 也就没有数据了
print(list(g2)) # 和g1同理
# 注:list中有for的调用,可以迭代遍历生成器元素
#结果:
# 1111
# [222]
# []
# []
访问生成器的另一种方法
使用yield from iterator语句
In[28]: def test():
...: l1 = [1, 2, 3, 4]
...: l2 = ['a', 'b', 'c', 'd']
...: yield from l1 #
...: yield from l2
...:
In[29]: g = test()
In[30]: for i in g:
...: print(i)
...:
1
2
3
4
a
b
c
d
python学习笔记:第12天 列表推导式和生成器的更多相关文章
- Python 速通爆肝、列表推导式、生成器、装饰器、生命游戏
列表推导式.赋值.切片(替换.插入).字符串处理与判断.enumerate().格式化字符串.读写文件.global 关键字.字符串startswith().类与对象.生成器.装饰器.Self.*ar ...
- Flutter学习笔记(12)--列表组件
如需转载,请注明出处:Flutter学习笔记(12)--列表组件 在日常的产品项目需求中,经常会有列表展示类的需求,在Android中常用的做法是收集数据源,然后创建列表适配器Adapter,将数据源 ...
- Python学习笔记(二)——列表
Python学习笔记(二)--列表 Python中的列表可以存放任何数据类型 >>> list1 = ['Hello','this','is','GUN',123,['I','Lov ...
- python之三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数
一 三元表达式.列表推导式.生成器表达式 一 三元表达式 name=input('姓名>>: ') res='SB' if name == 'alex' else 'NB' print(r ...
- python基础知识15---三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数
阅读目录 一 三元表达式.列表推导式.生成器表达式 二 递归与二分法 三 匿名函数 四 内置函数 五 阶段性练习 一. 三元表达式.列表推导式.生成器表达式 1 三元表达式 name=input('姓 ...
- Python进阶(四)----生成器、列表推导式、生成器推导式、匿名函数和内置函数
Python进阶(四)----生成器.列表推导式.生成器推导式.匿名函数和内置函数 一丶生成器 本质: 就是迭代器 生成器产生的方式: 1.生成器函数
- python 三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数
http://www.cnblogs.com/linhaifeng/articles/7580830.html 三元表达式.列表推导式.生成器表达式.递归.匿名函数.内置函数
- python 三元表达式、列表推导式、生成器表达式
一 三元表达式.列表推导式.生成器表达式 一 三元表达式 name=input('姓名>>: ') res='mm' if name == 'hahah' else 'NB' print( ...
- Python_迭代器、生成器、列表推导式,生成器表达式
1.迭代器 (1)可迭代对象 s1 = ' for i in s1: print(i) 可迭代对象 示例结果: D:\Python36\python.exe "E:/Python/课堂视频/ ...
随机推荐
- redis基本操作命令
前面我们看了redis的简介安装和数据类型,接下来介绍一下redis基本数据类型的操作命令: 一:redis键值对的管理和操作 DEL key 该命令用于在 key 存在时删除 key. DUMP k ...
- Event Driven Architecture
在微服务中使用领域事件 稍微回想一下计算机硬件的工作原理我们便不难发现,整个计算机的工作过程其实就是一个对事件的处理过程.当你点击鼠标.敲击键盘或者插上U盘时,计算机便以中断的形式处理各种外部事件 ...
- 高性能计算 GPU 等资料
从石油领域的应用看GPU.CPU算法效果比较 http://www.csdn.net/article/2011-06-21/300207 HPC 论坛 http://hpcbbs.it168.com ...
- windows下libevent的编译及使用
之前简单分析了libevent的源码,过了一段时间要用的时候发现完全忘记了..从头记录一下流程 1.编译 可以从github下载libevent的压缩包,解压后 修改以下三个文件,添加宏定义: 在以下 ...
- 使用combineReducers注意事项
一.从‘redux’包中引入combineReducers方法: import { combineReducers } from 'redux'; 二.针对state的不同属性写不同的reducer, ...
- 使用ant进行邮件发送,ant发送已存在的html文件
Jenkins上使用发送邮件功能一直有问题,放弃Jenkins配置,使用ant的进行发送邮件,参考文档可以: https://www.jianshu.com/p/04cfce59890a 我这里是要发 ...
- JAVA二叉树递归构造、二叉树普通遍历及递归遍历
二叉树类: package com.antis.tree; public class BinaryTree { int data; //根节点数据 BinaryTree left; //左子树 Bin ...
- PHP语言开发微信公众平台(订阅号)之curl命令(补充)
在之前的一篇随笔中,博主在调用curl命令上传文件时会经常出现上传方法过时的情况.如下图所示: 所以,我们只需要把上传方法换成创建CURLFile 类即可.如下所示 $ch = curl_init() ...
- Kali-linux应用更新和配置额外安全工具
本节将介绍更新Kali的过程和配置一些额外的工具.这些工具在后面的章节中将是有用的.Kali软件包不断地更新和发布之间,用户很快发现一套新的工具比最初在DVD ROM上下载的软件包更有用.本节将通过更 ...
- Knowledge Point 20180308 Dead Code
不知道有没有前辈注意过,当你编写一段“废话式的代码时”会给出一个Dead Code警告,点击警告,那么你所写的废物代码会被编译器消除,那么如果你不理睬这个警告呢?编译后会是什么样的呢?下面我们写点代码 ...