生成器

函数体内有yield选项的就是生成器,生成器的本质是迭代器,由于函数结构和生成器结构类似,可以通过调用来判断是函数还是生成器,如下:

def fun():
yield "我是生成器"
print(fun()) # 打印内容如下:
<generator object fun at 0x0000000002160ED0>

生成器的优点就是节省内存.

Python获取生成器的二种方式:

  • 通过函数获取生成器
  • 通过生成器推导式创建生成器

通过函数获取生成器

def fun():
print("fun")
yield "生成器"
g = fun()
print(g) # 打印函数名查看是否是生成器 # 打印内容如下:
<generator object fun at 0x0000000000510ED0>

从打印内容可以看出是生成器,但是发现生成器里面的内容没有被打印,那如何打印生成器内容呢?我们可以把生成器理解成迭代器的变异版,所以要打印生成器的内容,与迭代器类似,创建生成器对象后,可以使用生成器.__next__()来打印生成器内容,或者next()、send()等来打印生成器,如下:
使用.__next__()来打印生成器中的内容:

def fun():
print("我在yield 1的上面")
yield "yield 1"
print("我在yield 1的下面")
g = fun() # 创建生成器对象
print(g.__next__()) # 打印生成器里面的内容 # 打印内容如下
我在yield 1的上面
yield 1

从上面的打印结果可以发现yield下面的print语句没有被打印,到yield停止了。

def fun():
print("我在yield 1 上面")
yield "我是yield 1"
print("我在yield 1 下面")
yield "我是yield 2"
print("我在yield 2 下面")
g = fun() # 创建生成器对象
print(g.__next__())
print(g.__next__()) # 打印内容如下
我在yield 1 上面
我是yield 1
我在yield 1 下面
我是yield 2

由上面两个事例我们可以看出就是每next一次就执行一次yield上面的代码一次,yield下面的代码不会被执行,这就是生成器的惰性机制。

下面使用next()打印生成器内容:

def fun():
print("我在yield 1 上面")
yield "我是yield 1"
print("我在yield 1 下面")
yield "我是yield 2"
print("我在yield 2 下面") g = fun()
print(next(g)) # next(g)打印生成器内容
print(next(g)) # next(g)打印生成器内容 # 打印内容如下
我在yield 1 上面
我是yield 1
我在yield 1 下面
我是yield 2

与.__next__()功能类似

在使用send(参数)打印生成器内容:
send方法可以给上一层的yield传递一个值,如果上一个yield没有值的话send的参数将被忽略,如果有值yield的值将被改变成当前的参数,还有需要注意的地方就是如果send(参数)做为第一次迭代,由于上一层没有yield,所以没有办法传参,会导致出现错误,错误内容如下:
TypeError: can't send non-None value to a just-started generator
我们将send(None)作为第一次调用即可,然后在第二次调用时可以传适当的参数。如下:

def fun():
val = yield "我是yield 1"
print(val)
val = yield "我是yield 2"
print(val)
yield "我是yield 3"
print("我在yield 3 下面") g = fun()
print(g.send(None))
print(g.send("我是send 2"))
print(g.send("我是send 3")) # 打印内容如下
我是yield 1
我是send 2
我是yield 2
我是send 3
我是yield 3

使用for循环打印生成器所有内容。

def fun():
yield "我是yield 1"
yield "我是yield 2"
yield "我是yield 3" g = fun() # 创建生成器对象
for g_buf in g: # 使用for循环打印生成器对象
print(g_buf) # 打印内容如下
我是yield 1
我是yield 2
我是yield 3

yield可以返回任何数据类型,这里以列表为事例:

def fun():
list_1 = [1,2,3,4,5]
yield list_1 # 将整个列表作为返回值传给生成器对象
g = fun() # 创建生成器对象
print(g.__next__()) # 打印生成器对象 # 打印内容如下:
[1, 2, 3, 4, 5]

如果想要yield从列表中每次返回一个元素可以使用yield from 列表来实现,如下:

def fun():
list_1 = [1,2,3,4,5]
yield from list_1
g = fun() # 创建生成器对象
print(g.__next__()) # 打印生成器对象内容 # 打印内容如下:
1

可以发现只打印了列表中的一个元素,可以使用for循环打印所有内容:

def fun():
list_1 = [1,2,3,4,5]
yield from list_1
g = fun()
for g_buf in g:
print(g_buf) # 打印内容如下:
1
2
3
4
5

相当于执行了5次print(g.__next__())  打印生成器所有内容。

推导式

列表推导式

如给list_1列表赋值,常规做法如下:

list_1 = []
for num in range(10):
list_1.append(num)
print(list_1) # 打印内容如下
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

使用列表推导式如下:

list_1 = [num for num in range(10)]
list_2 = ["Python: %s" % num for num in range(3)]
print(list_1)
print(list_2) # 打印内容如下
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
['Python: 0', 'Python: 1', 'Python: 2']

列表推导式还可以进行筛选,如下:

list_1 = [num for num in range(20) if num < 5 or num == 15]
print(list_1) # 打印内容如下:
[0, 1, 2, 3, 4, 15]

升级一点,将一个嵌套列表中以"a"开头和以"h"开头的元素存放在一个空列表中,基础写法如下:

names = [['abc', 'abb', 'zzz'],["hello","world","xiaoming"]]
list_names = []
for name_1 in names:
if type(name_1) == list:
for name_2 in name_1:
if name_2.startswith("a") or name_2.startswith("h"):
list_names.append(name_2)
print(list_names) # 打印内容如下:
['abc', 'abb', 'hello']

使用列表推导法:

names = [['abc', 'abb', 'zzz'],["hello","world","xiaoming"]]
list_names = [name_2 for name_1 in names if type(name_1) for name_2 in name_1 if name_2.startswith("a") or\
name_2.startswith("h")] # 打印内容如下:
['abc', 'abb', 'hello']

生成器推导式

与列表推导式类似,只不过列表是使用[],生成器推导式使用的是()。

g_1 = (num for num in range(10))
print(g_1)
print(g_1.__next__())
print(g_1.__next__())
# 打印内容如下
<generator object <genexpr> at 0x0000000002FAACA8>
0
1

我们知道生成器是具有惰性的,每次只能获取一条数据,下面使用for循环打印所有内容:

g_1 = (num for num in range(10))
for num in g_1:
print(num,end=' ') # 打印内容如下
0 1 2 3 4 5 6 7 8 9

生成器的筛选与列表推导式用法一样,只不过是()。
如下:过滤1-20内的所有偶数。

g_1 = (num for num in range(20) if num % 2 == 0)
for i in g_1:
print(i,end=" ") # 打印内容如下
0 2 4 6 8 10 12 14 16 18

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

  • 列表推导式比较耗内存,一次性加载全部数据,生成器表达式几乎不占用内存,使用的时候才分配和使用内存。
  • 得到的值不一样,列表推导式得到的是一个列表全部数据,生成器表达式获取的是一个生成器。

字典推导式

list_1 = ["电视剧","电影"]
list_2 = ["上海滩","黄飞鸿"]
dict_1 = {list_1[i]:list_2[i] for i in range(len(list_1))}
print(dict_1) # 打印内容如下:
{'电视剧': '上海滩', '电影': '黄飞鸿'}

集合推导式

集合的特点:无序、不重复所以集合推导式自带去重功能。

list_1 = [1,2,3,4,2,3,5]
set_1 = {i for i in list_1} # 集合推导式
print(set_1) # 打印内容如下:
{1, 2, 3, 4, 5}

总结:

  • 推导式有列表推导式、生成器推导式、字典推导式、集合推导式。
  • 生成器表达式:(结果 for 变量 in 可迭代对象 if 条件筛选)
  • 生成器表达式可以直接获取到生成器对象,生成器对象具有惰性,每次只能获取一条内容,可以使用for循环打印生成器所有的内容。

下一篇:装饰器,常用内置函数:https://www.cnblogs.com/caesar-id/p/10328512.html

Python生成器、推导式之前襟后裾的更多相关文章

  1. Python——生成器&推导式

    生成器 生成器的本质就是迭代器,那么还为什么有生成器呢,两者唯一的不同就是迭代器都是Python给你提供能够的已经写好的工具或者通过数据转化得来的.而生成器是需要我们自己用Python代码构建的工具. ...

  2. Python生成器/推导式/生成器表达式

    一   生成器 生成器的本质就是迭代器 生成器的特点和迭代器一样,取值方式和迭代器一样(__next__(),  send():  给上一个yield传值) 生成器一般由生成器函数或者生成器表达式来创 ...

  3. python 生成器推导式与列表推导式的区别

    生成器表达式现用现生成,列表推导式一次性生成静态数据 L = [2, 3, 5, 7] L2 = (x**2+1 for x in L) it = iter(L2) print(next(it)) L ...

  4. 12.Python略有小成(生成器,推导式,内置函数,闭包)

    Python(生成器,推导式,内置函数,闭包) 一.生成器初始 生成器的本质就是迭代器,python社区中认为生成器与迭代器是一种 生成器与迭代器的唯一区别,生成器是我们自己用python代码构建成的 ...

  5. Python进阶(四)----生成器、列表推导式、生成器推导式、匿名函数和内置函数

    Python进阶(四)----生成器.列表推导式.生成器推导式.匿名函数和内置函数 一丶生成器 本质: ​ 就是迭代器 生成器产生的方式: ​ 1.生成器函数

  6. 记录我的 python 学习历程-Day12 生成器/推导式/内置函数Ⅰ

    一.生成器 初识生成器 生成器的本质就是迭代器,在python社区中,大多数时候都把迭代器和生成器是做同一个概念. 唯一的不同就是: 迭代器都是Python给你提供的已经写好的工具或者通过数据转化得来 ...

  7. Python函数04/生成器/推导式/内置函数

    Python函数04/生成器/推导式/内置函数 目录 Python函数04/生成器/推导式/内置函数 内容大纲 1.生成器 2.推导式 3.内置函数(一) 4.今日总结 5.今日练习 内容大纲 1.生 ...

  8. Python之路-迭代器 生成器 推导式

    迭代器 可迭代对象 遵守可迭代协议的就是可迭代对象,例如:字符串,list dic tuple set都是可迭代对象 或者说,能被for循环的都是可迭代对象 或者说,具有对象.__iter__方法的都 ...

  9. python 列表推导式,生成器推导式,集合推导式,字典推导式简介

    1.列表推导式multiples = [i for i in range(30) if i % 2 is 0]names = [[],[]]multiples = [name for lst in n ...

随机推荐

  1. Jmeter-常用线程组设置及场景运行时间计算

    Jmeter中通过线程组来模拟大用户并发场景,今天主要介绍三个常用的线程组,帮助我们设计更加完善的测试场景,另外介绍下场景执行时间如何计算. 一.Thread Group 取样器错误后要执行的动作   ...

  2. Git的使用--如何将本地项目上传到Github(三种简单、方便的方法)

    一.第一种方法: 1.首先你需要一个github账号,所以还没有的话先去注册吧! https://github.com/ 我们使用git需要先安装git工具,这里给出下载地址,下载后一路(傻瓜式安装) ...

  3. 利用Grafana展示zabbix数据

    一.系统搭建(以Centos7为例)因为我们的主要目的是展示zabbix的数据,所以建议大家直接在zabbix的服务器上搭建这个系统,亲测两系统无冲突,这样部署的好处是两系统间的数据传输更快,前端展示 ...

  4. redis 系列9 对象类型(字符串,哈希,列表,集合,有序集合)与数据结构关系

    一.概述 在前面章节中,主要了解了 Redis用到的主要数据结构,包括:简单动态字符串.链表(双端链表).字典.跳跃表. 整数集合.压缩列表(后面再了解).Redis没有直接使用这些数据结构来实现键值 ...

  5. hdu:2030.汉字统计

    Problem Description 统计给定文本文件中汉字的个数.   Input 输入文件首先包含一个整数n,表示测试实例的个数,然后是n段文本.   Output 对于每一段文本,输出其中的汉 ...

  6. 【Python3爬虫】微博用户爬虫

    此次爬虫要实现的是爬取某个微博用户的关注和粉丝的用户公开基本信息,包括用户昵称.id.性别.所在地和其粉丝数量,然后将爬取下来的数据保存在MongoDB数据库中,最后再生成几个图表来简单分析一下我们得 ...

  7. .NET之消息推送(SignalR即时通讯实现)

    前言 ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端可以互相通知 ...

  8. MongoDB Export & Import

    在使用MongoDB数据库的过程中,避免不了需要将数据进行导入和导出的工作,下面为具体的用法.注意 不同的数据库版本可能存在略微的差异,所以在使用时,先查看 --help 来进行确认.下面的为3.6版 ...

  9. springboot+mybatis+dubbo+aop日志终结篇

    之前的几篇文章把dubbo服务层都介绍完毕,本篇文章咱们主要写web层如何调用服务层的方法.文章底部附带源码. 启动服务 服务启动时,会向zk注册自己提供的服务,zk则会记录服务提供者的IP地址以及暴 ...

  10. 阿里云ACE共创空间——MQ消息队列产品测试

    一.产品背景消息队列是阿里巴巴集团自主研发的专业消息中间件. 产品基于高可用分布式集群技术,提供消息订阅和发布.消息轨迹查询.定时(延时)消息.资源统计.监控报警等一系列消息云服务,是企业级互联网架构 ...