Python全栈开发记录_第六篇(生成器和迭代器)
说生成器之前先说一个列表生成式:[x for x in range(10)] ->[0,1,2....,9]这里x可以为函数(因为对python而言就是一个对象而已),range(10)也可以换成可迭代对象。
如果说有一天我们的数据量很大呢?range(10000000)甚至更大呢?那我们会挤爆内存的,所以我们需要用到生成器(生成器是特殊的迭代器,当然就是可迭代对象了),因为生成器不会将所有数据存在内存中,只是保存了算法,刚刚说到的[x for x in range(10)]实际上改成(x for x in range(10))就成了一个生成器(generator)对象,如下:
g = (x for x in range(5))
print(g) # 这里是生成器对象
print(next(g)) # 取值,每次取一个,跟g.__next__一样,在py2中用g.next()
for i in g: # 循环取出剩余的值,为了增强效果所以加了#
print("#"+str(i))
结果就是:
<generator object <genexpr> at 0x0000000001E0EFC0>
0
#
#
#
#
实际上这是生成器一种生成的方法(生成器表达式),一般我们调用的话也是用for循环,不会一个一个next,接着来看第二种生成器产生的方法,就是生成器函数,简单的来讲就是只要函数里面带yield就是生成器。
看一下下面的这个就是一个生成器:
def foo():
print("num1")
yield 1
print("num2")
yield 2 g = foo() # 此时g就是生成器
print(g) # <generator object foo at 0x000000000216EFC0>
for i in g:
print(i) '''
下面是结果:
<generator object foo at 0x0000000001E1EFC0>
num1
1
num2
2
'''
还有比如这个是我们之前写过的斐波那契数列,正常是下面这种实现的。我们也可以通过yield实现
def feibo(max):
n, before, after = 0, 0, 1
while max > n:
print(after)
before, after = after, before+after # 先算等号右边的,所以第一次右边是1,1,然后再赋值给左边
n += 1
下面就是yield版本:
def feibo(max):
n, before, after = 0, 0, 1
while max > n:
yield after
before, after = after, before+after
n += 1 g = feibo(5)
print(g)
for i in g:
print(i) '''
#结果如下:
<generator object feibo at 0x0000000001DEEFC0>
1
1
2
3
5
'''
这里说了yield关键字可以暂停函数并且返回一个值,既然生成器可以返回值给外部,那么外部能不能传值给生成器呢?答案是当然可以的,可以通过send方法将值传入上一次被挂起的yield语句的返回值,当然这样说可能你一脸懵B,没事,举个例子,看下面:
def foo():
print("num1")
value1 = yield 1
print(value1)
print("num2")
value2 = yield 2
print(value2) # 实际上每个函数最后面都有个return None,如果我们不写return的话 g = foo() # 这个是生成器对象
next(g) # 先跑到yield 1的位置,但是又yield所以在这里停住了,然后返回了1,这一步也可以用g.send(None)
g.send("liu") # 遇到了send或者next就会继续返回上一次的位置,此时将"liu"赋值给了value1然后打印value1和num2,走到yield 2停住,返回2
g.send("kang") # 接着这里kang字符串赋值给value2,注意在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以执行send方法会报错,所以后面就直接报错了(碰到return也会报错)
执行结果如下:
num1
liu
num2
kang
Traceback (most recent call last):
File "D:/BaiduNetdiskDownload/python/Python_code/week_04/generate_test.py", line 51, in <module>
g.send("kang")
StopIteration
现在应该对send的作用有所了解了吧,这里我们来做个小小练习,通过上述学到的生成器来简单写个伪并发边生产边消费的例子吧,可以先想一想然后再往下看:
"""
需求:需要实现生产者消费者伪并发 分析:
1、肯定是需要两个函数分别为生产者和消费者
2、生产者需要生产东西,然后再发生(send)给消费者
3、消费者吃完后需要停一下(yield) """ # 消费者
def comsumer(name):
while True:
food = yield
print("%s eat %s" % (name, food)) # 生产者
def producer(name):
c1 = comsumer("xiaoming")
c2 = comsumer("xiaopang")
next(c1) # 在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以不能直接就send值,可以send(None)
next(c2)
for i in range(1,10,2):
print("%s在生产商品%s和%s" % (name, i, i+1))
c1.send(i)
c2.send(i+1) producer("liu")
执行结果如下:
liu在生产商品1和2
xiaoming eat 1
xiaopang eat 2
liu在生产商品3和4
xiaoming eat 3
xiaopang eat 4
liu在生产商品5和6
xiaoming eat 5
xiaopang eat 6
liu在生产商品7和8
xiaoming eat 7
xiaopang eat 8
liu在生产商品9和10
xiaoming eat 9
xiaopang eat 10
总的来说,send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互。但是需要注意,在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以执行send方法会报错。
上面我们提到了三个概念,分别是生成器、迭代器还有可迭代对象,那这几个是啥关系呢?这个图中包含了容器(container)、可迭代对象(iterable)、迭代器(iterator)、生成器(generator)、列表/集合/字典推导式(list,set,dict comprehension)众多概念的关系,我感觉非常不错就借鉴过来了。

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用 in , not in 关键字判断元素是否包含在容器中。
# 生成器都是迭代器,反之不成立
"""
1、可迭代对象都有__iter__()方法,而迭代器除了__iter__()方法还有__next__()方法
2、可迭代对象l = [1,2,3]可以通过iter(l)变成迭代器,这样就可以使用__next__()方法
"""
l = [1,2,3]
d = iter(l)
"""
for循环干的活:
1、调用可迭代对象的iter方法返回迭代器对象
2、循环调用迭代器对象的next方法
3、处理StopIteration异常
"""
for i in d:
print(i) from collections import Iterator,Iterable
print(isinstance(l, Iterable)) # 判断l是否为可迭代对象 True
print(isinstance(d, Iterable)) # 判断d是否为可迭代对象(迭代器一定是可迭代对象,因为一定有iter方法) True
print(isinstance(l, Iterator)) # 判断l是否为迭代器对象 False
print(isinstance(d, Iterator)) # 判断d是否为迭代器对象 True with open("qq") as f:
print(f.__next__())
print(isinstance(f, Iterator)) # True f是迭代器对象 """
练习1:使用文件读取,找出文件最长的长度 分析:
1、肯定是要先打开文件
2、长度的话肯定需要len方法,最长的可以用max """
答案:max(len(x.strip()) for x in open("qq"))
下面还有个小练习,是针对生成器的理解
def add(s, x):
return s + x def gen():
for i in range(4):
yield i base = gen()
for n in [1, 10]:
base = (add(i, n) for i in base) print list(base)
解析:
for n in [1, 10]:
base = (add(i, n) for i in base) 实际上最后在list(base)的时候才最终将值全部取出来了,前面一直都是保存着算法而已,所以对于第二次就是base = (add(i, n) for i in (add(i, n) for i in g)),所以到10的时候就直接讲10带入了,所以结果就是[20, 21, 22, 23],
要注意生成器是保存着算法,不取值的时候不要去计算值。
Python全栈开发记录_第六篇(生成器和迭代器)的更多相关文章
- Python全栈开发记录_第三篇(linux(ubuntu)的操作)
该篇幅主要记录linux的操作,常见就不记录了,主要记录一些不太常用.难用或者自己忘记了的点. 看到https://www.cnblogs.com/resn/p/5800922.html这篇幅讲解的不 ...
- Python全栈开发记录_第八篇(模块收尾工作 json & pickle & shelve & xml)
由于上一篇篇幅较大,留下的这一点内容就想在这里说一下,顺便有个小练习给大家一起玩玩,首先来学习json 和 pickle. 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不过, ...
- Python全栈开发记录_第七篇(模块_time_datetime_random_os_sys_hashlib_logging_configparser_re)
这一篇主要是学习python里面的模块,篇幅可能会比较长 模块的概念:在Python中,一个.py文件就称之为一个模块(Module). 模块一共三种: python标准库 第三方模块 应用程序自定义 ...
- Python全栈开发记录_第四篇(集合、函数等知识点)
知识点1:深拷贝和浅拷贝 非拷贝(=赋值:数据完全共享,内存地址一样,修改一个另一个也变化) 浅拷贝:数据半共享(复制其数据独立内存存放,但是只拷贝成功第一层)像[[1,2],3,4]如果修改列表中列 ...
- Python全栈开发记录_第十篇(反射及选课系统练习)
反射机制:反射就是通过字符串的形式,导入模块:通过字符串的形式,去模块中寻找指定函数,对其进行操作.也就是利用字符串的形式去对象(模块)中操作(查找or获取or删除or添加)成员,一种基于字符串的事件 ...
- Python全栈开发记录_第五篇(装饰器)
单独记录装饰器这个知识点是因为这个知识点是非常重要的,必须掌握的(代码大约150行). 了解装饰器之前要知道三个知识点 作用域,上一篇讲到过顺序是L->E->G->B 高阶函数: 满 ...
- Python全栈开发记录_第一篇(循环练习及杂碎的知识点)
Python全栈开发记录只为记录全栈开发学习过程中一些难和重要的知识点,还有问题及课后题目,以供自己和他人共同查看.(该篇代码行数大约:300行) 知识点1:优先级:not>and 短路原则:a ...
- Python全栈开发记录_第九篇(面向对象(类)的学习)
有点时间没更新博客了,今天就开始学习类了,今天主要是面向对象(类),我们知道面向对象的三大特性,那就是封装,继承和多态.内容参考该博客https://www.cnblogs.com/wupeiqi/p ...
- Python全栈开发记录_第二篇(文件操作及三级菜单栏增删改查)
python3文件读写操作(本篇代码大约100行) f = open(xxx.txt, "r", encoding="utf-8") 不写“r”(只读)默认是只 ...
随机推荐
- sublime text 3搭建python
1.ST3下载地址: http://www.sublimetext.com/3 2.安装Sublime Text Build 3114 Setup.exe应用程序. 3.ST3的工具优点就是轻量级,简 ...
- linux文本编辑器vim大全
linux基础之vim编辑器 1.vim编辑器 基本介绍 vim编辑器的前身叫做vi.vi的英文名:Visual Interface.中文解释文本编辑器,你不应该用他去打开二进制可执行文件 文本编辑 ...
- 如何注册Tomcat到Window Service服务
win+R打开运行窗口,输入cmd打开dos窗口,使用cd命令将位置切换到tomcat路径下的bin文件,本机是F盘下. 先输入F:回车进入F盘,然后输入命令cd F:\apache-tomcat-5 ...
- branchynet
提前退出 神经网络 https://gitlab.com/kunglab/branchynet/tree/master 修改梯度 gradients 方法,参考: https://stackoverf ...
- 汇编实验2(又是作业emm)
实验任务:学会使用debug 1.使用Debug,将程序段写入内存: 首先对0021:0000~0021:000F的内存赋值 这里我赋的值是 11 12 13 14 15 16 17 18 输入mov ...
- WebView 判断放大缩小操作
using Android.App; using Android.Widget; using Android.OS; using Android.Content; using Android.Runt ...
- Vector Math for 3D Computer Graphics (Bradley Kjell 著)
https://chortle.ccsu.edu/VectorLessons/index.html Chapter0 Points and Lines (已看) Chapter1 Vectors, P ...
- docker nginx letsencrypt
https越来越流行了,但免费的证书一般是一年有效期.一般是够用了,但懒人都想一劳永逸, 有个免费证书颁发机构是letsencrypt.它是开源,并且完全免费的,它颁发的证书已经被几乎所有的浏览器所认 ...
- C goto
http://c.biancheng.net/view/266.html 当程序遇到 goto 后, 会无条件跳转到标签后出,然后程序按照顺序执行 例子: #include <stdio.h&g ...
- CRMEB提示:系统错误 lnterface SessionUpdateTimestampHandlerlnterface not found
安装CRMEB系统时,公众号网页提示:系统错误 lnterface SessionUpdateTimestampHandlerlnterface not found 怎么办? 解决方法:PHP更换为7 ...