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”(只读)默认是只 ...
随机推荐
- python学习6---字典合并
1.针对python3版本 d1={'gao':1,'luo':2} d2={'liu':3,'jia':4} dictMerged1=dict(d1,**d2) print(dictMerged1) ...
- java将字符串存入oracle的Blob中
Blob内存放的是字节数组,需使用String的getBytes获得该字符串的字节数组(注意字符集编码),然后存入Blob. Oracle的Blob字段比较特殊,他比long字段的性能要好很多,可以用 ...
- 转:Eclipse Memory Analyzer入门学习笔记
原文地址:https://blog.csdn.net/cc907566076/article/details/79108782 Eclipse Memory Analyzer是一个快速而功能丰富的Ja ...
- vue H5页面在微信浏览器打开软键盘关闭导致页面空缺的问题。
methods:{ inputBlur () { // window.scroll(0, 0); setTimeout(() => { // alert(1); if (document.act ...
- hdu 1518 BFS
Given a set of sticks of various lengths, is it possible to join them end-to-end to form a square? I ...
- hibernate的lazy初始化结果
package com.ehcache; import java.io.Serializable; public class User implements Serializable{ private ...
- 转载 JAVA gc垃圾回收机制
thanks:https://m.oschina.net/u/123553 一.GC概要 JVM堆相关知识 为什么先说JVM堆? JVM的堆是Java对象的活动空间,程序中的类的对象从中分 ...
- 简单kmp算法(poj3461)
题目简述: 给你两个字符串p和s,求出p在s中出现的次数. 思路简述: 在介绍看BF算法时,终于了解到了大名鼎鼎的KMP算法,结果属于KMP从入门到放弃系列,后来看了几位大神的博客,似乎有点懂了.此题 ...
- centos 终端字体错位个别字母中间有间隔的解决
问题描述: linux系统:centos 终端:图形界面终端,通过startx启动 现象:通过终端输入的字体有重叠,字母之间的间隔也很大.由于字体安装不正确导致. 解决方法:通过下面字体的安装命令可以 ...
- GraphQL搭配MongoDB入门项目实战
什么是GraphQL GraphQL 是一种面向 API 的查询语言.在互联网早期,需求都以 Web 为主,那时候数据和业务需求都不复杂,所以用 RestAPI 的方式完全可以满足需求.但是随着互联网 ...