1. 生成器

什么是⽣成器?⽣成器实质就是迭代器。
在python中有三种⽅式来获取⽣成器:
1. 通过⽣成器函数
2. 通过各种推导式来实现⽣成器
3. 通过数据的转换也可以获取⽣成器
⾸先,我们先看⼀个很简单的函数:
def func():
  print("")
  return 222
ret = func()
print(ret)

结果:
111
222
将函数中的return换成yield就是⽣成器
def func():
  print("")
  yield 222
ret = func()
print(ret)

结果:
<generator object func at 0x10567ff68>
运⾏的结果和上⾯不⼀样,为什么呢?由于函数中存在了yield,那么这个函数就是⼀个⽣成器
函数。这个时候,我们再执⾏这个函数的时候,就不再是函数的执⾏了,⽽是获取这个⽣成器。
如何使⽤呢? 想想迭代器,⽣成器的本质是迭代器,所以,我们可以直接执⾏__next__()来执⾏以下⽣成器。
def func():
  print("")
  yield 222
gener = func() # 这个时候函数不会执⾏. ⽽是获取到⽣成器
ret = gener.__next__() # 这个时候函数才会执⾏. yield的作⽤和return⼀样. 也是返回数据
print(ret)

结果:
111
222
那么我们可以看到,yield和return的效果是⼀样的,有什么区别呢? yield是分段来执⾏⼀个
函数,return呢? 直接停⽌执⾏函数。
def func():
  print("")
  yield 222
  print("")
  yield 444
gener = func()
ret = gener.__next__()
print(ret)
ret2 = gener.__next__()
print(ret2)
ret3 = gener.__next__() # 最后⼀个yield执⾏完毕. 再次__next__()程序报错, 也就是说. 和return⽆关了.
print(ret3)

结果:
111
Traceback (most recent call last):
222
333
  File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in
<module>
444
  ret3 = gener.__next__() # 最后⼀个yield执⾏完毕. 再次__next__()程序报错, 也就是说. 和return⽆关了.
StopIteration
当程序运⾏完最后⼀个yield,那么后⾯继续进⾏__next__()程序会报错。好了⽣成器说完了,⽣成器有什么作⽤呢? 我们来看这样⼀个需求,老男孩向JACK JONES订
购10000套学⽣服,JACK JONES就比较实在,直接造出来10000套衣服。
def cloth():
  lst = []
  for i in range(0, 10000):
    lst.append("⾐服"+str(i))
  return lst
cl = cloth()
但是呢,问题来了。老男孩现在没有这么多学⽣啊,⼀次性给我这么多,我往哪⾥放啊. 很尴尬啊,最好的效果是什么样呢? 我要1套,你给我1套,⼀共10000套,是不是最完美的。
 
def cloth():
  for i in range(0, 10000):
    yield "⾐服"+str(i)
cl = cloth()
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
区别: 第⼀种是直接⼀次性全部拿出来,会很占⽤内存,第⼆种使⽤⽣成器,⼀次就⼀个. ⽤多少⽣成多少。⽣成器是⼀个⼀个的指向下⼀个,不会回去,__next__()到哪,指针就指到哪⼉,下⼀次继续获取指针指向的值。
接下来我们来看send⽅法,send和__next__()⼀样都可以让⽣成器执⾏到下⼀个yield。
def eat():
  print("我吃什么啊")
  a = yield "馒头"
  print("a=",a)
  b = yield "⼤饼"
  print("b=",b)
  c = yield "⾲菜盒⼦"
  print("c=",c)
  yield "GAME OVER"

gen = eat() # 获取⽣成器
ret1 = gen.__next__()
print(ret1)
ret2 = gen.send("胡辣汤")
print(ret2)
ret3 = gen.send("狗粮")
print(ret3)
ret4 = gen.send("猫粮")
print(ret4)
send和__next__()区别:
1. send和next()都是让⽣成器向下走⼀次。
2. send可以给上⼀个yield的位置传递值,不能给最后⼀个yield发送值,在第⼀次执⾏⽣成器代码的时候不能使⽤send()。
⽣成器可以使⽤for循环来循环获取内部的元素:
def func():
  print(111)
  yield 222
  print(333)
  yield 444
  print(555)
  yield 666

gen = func()
for i in gen:
  print(i)
  
结果:
111
222
333
444
555
666

2. 列表推导式, ⽣成器表达式以及其他推导式

⾸先我们先看⼀下这样的代码,给出⼀个列表,通过循环,向列表中添加1-14 :
lst = []
for i in range(1, 15):
  lst.append(i)
print(lst)
替换成列表推导式:
lst = [i for i in range(1, 15)]
print(lst)
列表推导式是通过⼀⾏来构建你要的列表,列表推导式看起来代码简单,但是出现错误之后很难排查。
列表推导式的常⽤写法:
[ 结果 for 变量 in 可迭代对象]
例,从python1期到python14期写入列表lst:
lst = ['python%s' % i for i in range(1,15)]
print(lst)
我们还可以对列表中的数据进⾏筛选
筛选模式:
[ 结果 for 变量 in 可迭代对象 if 条件 ]
# 获取1-100内所有的偶数
lst = [i for i in range(1, 100) if i % 2 == 0]
print(lst)
⽣成器表达式和列表推导式的语法基本上是⼀样的,只是把[]替换成()
gen = (i for i in range(10))
print(gen)

结果:
<generator object <genexpr> at 0x106768f10>
打印的结果就是⼀个⽣成器,我们可以使⽤for循环来循环这个⽣成器:
gen = ("麻花藤我第%s次爱你" % i for i in range(10))
for i in gen:
  print(i)
⽣成器表达式也可以进⾏筛选:# 获取1-100内能被3整除的数
gen = (i for i in range(1,100) if i % 3 == 0)
for num in gen:
  print(num)

# 100以内能被3整除的数的平⽅
gen = (i * i for i in range(100) if i % 3 == 0)
for num in gen:
  print(num)

# 寻找名字中带有两个e的⼈的名字
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven','Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

# 不⽤推导式和表达式
result = []
for first in names:
  for name in first:
    if name.count("e") >= 2:
      result.append(name)
print(result)

# 推导式
gen = (name for first in names for name in first if name.count("e") >= 2)
for name in gen:
  print(name)
⽣成器表达式和列表推导式的区别:
1. 列表推导式比较耗内存,⼀次性加载,⽣成器表达式⼏乎不占⽤内存,使⽤的时候才分配和使⽤内存。
2. 得到的值不⼀样,列表推导式得到的是⼀个列表,⽣成器表达式获取的是⼀个⽣成器。
举个栗⼦。
同样⼀篮⼦鸡蛋,列表推导式: 直接拿到⼀篮⼦鸡蛋。⽣成器表达式: 拿到⼀个老⺟鸡,需要鸡蛋就给你下鸡蛋。
⽣成器的惰性机制: ⽣成器只有在访问的时候才取值,说⽩了,你找他要他才给你值,不找他要,他是不会执⾏的。
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同理
深坑==> ⽣成器,要值得时候才拿值。
字典推导式:
根据名字应该也能猜到,推到出来的是字典。
# 把字典中的key和value互换
dic = {'a': 1, 'b': ''}
new_dic = {dic[key]: key for key in dic}
print(new_dic)

# 在以下list中. 从lst1中获取的数据和lst2中相对应的位置的数据组成⼀个新字典
lst1 = ['jay', 'jj', 'sylar']
lst2 = ['周杰伦', '林俊杰', '邱彦涛']
dic = {lst1[i]: lst2[i] for i in range(len(lst1))}
print(dic)
集合推导式:
集合推导式可以帮我们直接⽣成⼀个集合。集合的特点: ⽆序,不重复,所以集合推导式⾃带去重功能。
lst = [1, -1, 8, -8, 12]
# 绝对值去重
s = {abs(i) for i in lst}
print(s)
总结: 推导式有,列表推导式,字典推导式,集合推导式,没有元组推导式。
⽣成器表达式:
(结果 for 变量 in 可迭代对象 if 条件筛选)
⽣成器表达式可以直接获取到⽣成器对象,⽣成器对象可以直接进⾏for循环,⽣成器具有
惰性机制。

python基础(14):生成器、列表推导式的更多相关文章

  1. python基础-三元表达式/列表推导式/生成器表达式

    1.三元表达式:如果成立返回if前的内容,如果不成立返回else的内容 name=input('姓名>>: ') res='SB' if name == 'alex' else 'NB' ...

  2. Python基础8:列表推导式(list)字典推导式(dict) 集合推导式(set)

    推导式分为列表推导式(list),字典推导式(dict),集合推导式(set)三种 1.列表推导式也叫列表解析式.功能:是提供一种方便的列表创建方法,所以,列表解析式返回的是一个列表格式:用中括号括起 ...

  3. 『Python基础-12』各种推导式(列表推导式、字典推导式、集合推导式)

    # 『Python基础-12』各种推导式(列表推导式.字典推导式.集合推导式) 推导式comprehensions(又称解析式),是Python的一种独有特性.推导式是可以从一个数据序列构建另一个新的 ...

  4. python 三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数

    http://www.cnblogs.com/linhaifeng/articles/7580830.html 三元表达式.列表推导式.生成器表达式.递归.匿名函数.内置函数

  5. python 三元表达式、列表推导式、生成器表达式

    一 三元表达式.列表推导式.生成器表达式 一 三元表达式 name=input('姓名>>: ') res='mm' if name == 'hahah' else 'NB' print( ...

  6. Python三元表达式、列表推导式、生成器表达式

    1. 三元表达式 name=input('姓名>>: ') res='SB' if name == 'aaaa' else 'NB' print(res) 2. 列表推导式 #1.示例 e ...

  7. python note 12 生成器、推导式

    1.生成器函数 # 函数中如果有yield 这个函数就是生成器函数. 生成器函数() 获取的是生成器. 这个时候不执行函数# yield: 相当于return 可以返回数据. 但是yield不会彻底中 ...

  8. python基础学习1-列表推导式和字典推导式

    #!/usr/bin/env python # -*- coding:utf-8 -*- #列表推导式 取得100内可以被2整除且不能被3整除的数字 a=[i for i in range(100) ...

  9. python高级编程之列表推导式

    1. 一个简单的例子 在Python中,如果我们想修改列表中所有元素的值,可以使用 for 循环语句来实现. 例如,将一个列表中的每个元素都替换为它的平方: >>> L = [1, ...

随机推荐

  1. jQuery-点击返回顶部

    在页面上,有时需要点击某个图标钮实现返回顶部的效果. 实现方式如下,给图标按钮增加名叫goTop-hook的类. // 点击返回顶部 $(window).scroll(function() { if ...

  2. 【ST开发板评测】Nucleo-F411RE开箱报告

    前言 面包板又举办开发板试用活动了,很荣幸能获得一块ST官方的Nucleo-F411RE开发板,感谢面包板社区和ST意法半导体的赞助,这是我第一次试用官方的开发板,收到板子后查了一些关于ST官方开发板 ...

  3. 多线程七 AQS

    一 . 简介AQS AQS简介 在同步组件的实现中,AQS是核心部分,同步组件的实现者,通过使用AQS提供的模板方法 实现同步组件语义 AQS实现了对同步状态的管理以及阻塞线程进行排队,等待通知等等一 ...

  4. Selenium(十六):unittest单元测试框架(二) 初识unittest(续)

    1. 认识unittest(续) 关于unittest单元测试框架,还有一些问题值得进一步探讨.你可能在前一章的学习过程中产生了一些疑问,也许你会在本节中找到答案. 1.1 用例执行的顺序 用例的执行 ...

  5. MySQL数据库:注释及数据类型

    注释 /* 多行注释 */ # 单行注释 -- 单行注释(注意:两个但横线后面必须加空格) 数据类型 1.整形 ​ tinyint.smallint.mediumint.int.bigint ​ 小整 ...

  6. MyBatis之接口绑定方案及多参数传递

    1.说明   所谓的MyBatis接口绑定,指的是实现创建一个接口后,把mapper.xml 由mybatis 生成接口的实现类,通过调用接口对象就可以获取mapper.xml 中编写的sql.在SS ...

  7. 8.1 Spark MLlib简介

    一.什么是机器学习 机器学习可以看做是一门人工智能的科学,该领域的主要研究对象是人工智能.机器学习利用数据或以往的经验,以此优化计算机程序的性能标准. 机器学习强调三个关键词:算法.经验.性能 二.基 ...

  8. Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises)

    传送门 A. Optimal Currency Exchange 枚举一下就行了. Code #include <bits/stdc++.h> using namespace std; t ...

  9. LINUX上安装JDK+tomcat+mysql操作笔记

    1.环境准备: 1-1.centos 64位(本人的虚拟机安装此系统),安装步骤和网络配置已经在前两篇记录. 1-2.JDK 版本1.8 1-3.tomcat压缩包 1-4.CRT远程连接工具(可用其 ...

  10. AcWing 836. 合并集合

    地址  https://www.acwing.com/problem/content/838/ 一共有n个数,编号是1~n,最开始每个数各自在一个集合中. 现在要进行m个操作,操作共有两种: “M a ...