python学习日记(生成器函数进阶)
迭代器和生成器的概念
迭代器
对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数。iter()是python内置函数。
iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的元素。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。 迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的_next_方法(Python3中是对象的_next_方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现_iter_方法,而_iter_方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的_iter_方法返回自身self即可。
迭代器定义
1,迭代器协议:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。 2,可迭代对象:实现了迭代器协议对象。list、tuple、dict都是Iterable(可迭代对象),但不是Iterator(迭代器对象)。但可以使用内建函数iter(),把这些都变成Iterable(可迭代器对象)。 3,for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束
迭代器的术语解释
生成器
作用:
作用:延迟操作。也就是在需要的时候才产生结果,不是立即产生结果。
注意事项:
>生成器是只能遍历一次的。
>生成器是一类特殊的迭代器。
分类:
第一类:生成器函数:还是使用 def 定义函数,但是,使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。
第二类:生成器表达式:类似于列表推导,只不过是把一对大括号[]变换为一对小括号()。但是,生成器表达式是按需产生一个生成器结果对象,要想拿到每一个元素,就需要循环遍历。
生成器
对于生成器来说,看到yield一定要有在这里挂起的概念!!!
生成器
send
一、
def generator():
print(123)
f = yield 'aaa'#3、f用来接收send传来的参数
print(f)
print(456)
yield 'bbb'
g = generator()
print(g.__next__())#1、第一次算是激活,然后在yield处挂起
print(g.send('new day'))#2、从上次的挂起处开始,传递一个参数给yield 返回值
注意:在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,执行send方法会报错,除非你传的是None。
二、
想给最后一个yield使用send,那么最后你还应该再添加一个yield语句防止StopIteration异常。
def generator():
print(123)
f1 = yield 'aaa'#3、f用来接收send传来的参数
print(f1)
print(456)
f2 = yield 'bbb'
print(f2)
yield ''#不显示None
g = generator()
print(g.__next__())#1、第一次算是激活,然后在yield处挂起
print(g.send('new day'))#2、从上次的挂起处开始,传递一个参数给yield 返回值
print(g.send('star lord'))
练习:获取动态平均值
实现:依次输入10、20、30,所求的平均值为:10、15、20。
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg#num = 10
count += 1#count = 1
sum += num#sum = 10
avg = sum/count#avg = 10/1=10
a = average()#1、返回迭代器
a1 = a.__next__()#2、在yield avg 这里挂起,返回的avg = 0
print(a1)
a1 = a.send(10)#3、挂起处开始,10传给num,count =0+1=1,sum = 10,avg = 10/1=10,执行直到遇见yield,返回avg,a1 接收 avg,重新挂起
print(a1)
a1 = a.send(20)#4、挂起处开始,20传给num,a1 接收 avg,在下一次yield avg重新挂起
print(a1)
a1 = a.send(30)
print(a1)
激活生成器的装饰器
def init(f):#f == average
def inner(*args,**kwargs):
g = f(*args,**kwargs)#a = average(),返回的是生成器
next(g)#生成器激活
return g#再返回给调用inner处
return inner
@init
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg#num = 10
count += 1#count = 1
sum += num#sum = 10
avg = sum/count#avg = 10/1=10
avg = average()#avg = inner()
#省去了激活的步骤,next
print(avg.send(10))
print(avg.send(40))
yield from
一、
def generator():
a = ''
b = [4,5,6]
for i in a:
yield i
for j in b:
yield j
g = generator()
for k in g:
print(k,end=' ')
二、
def generator():
a = ''
b = [4,5,6]
yield from a
yield from b
g = generator()
for k in g:
print(k,end=' ')
生成器的表达式
列表推导式
列表解析:
apple = ['apple%s'%i for i in range(5)]
print(apple)
生成器表达式
把列表解析里面的 [] 换成 () 就变成了生成器表达式。
little_girl = ('蘑菇%s'%i for i in range(10))
l = little_girl
print(l)
for i in l:
print(i,end=',')
注:生成器表达式几乎不占用内存,列表解析可能会占用大量内存。
表达式的筛选
一、30以内能被3整除的数字的平方。
l = [i**2 for i in range(31) if i%3 == 0]
print(l)
二、生成器同理,只是换了括号。
嵌套筛选
找到嵌套列表中名字含有两个‘e’的所有名字。
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
re = [i for li in names for i in li if i.count('e') == 2]
print(re)
其他推导式
字典推导式
一、
将一个字典的key 和value 对调。
dic = {'libai':'happy','dufu':'sad'}
d = {dic[k]: k for k in dic}
print(d)
二、
合并大小写对应的value,将key统一成小写。
dic = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
d = {k.lower():dic.get(k.lower(),0)+dic.get(k.upper(),0) for k in dic}#字典get方法,不存在,返回0
print(d)
集合推导式
计算列表中数字的平方,并去重。
li = [1,-1,2,-2,3]
se = {i**2 for i in li}
print(se)
没有元组推导式是因为加了括号就变成了生成器推导式。
生成器有趣面试题
一、
def demo():
for i in range(4):
yield i g=demo() g1=(i for i in g)
g2=(i for i in g1) print(list(g1))
print(list(g2))
解析:首先、生成器的特性就是惰性的。当执行print(list(g1))之前,上面的语句虽都定义,但都未执行,执行print(list(g1)),g1作为生成器表达式从生成器g里面遍历取值,然后g1将取到的值拿出生成列表[0,1,2,3]。此时,g1也进行了一次遍历,不再有值了,所以print(list(g2))执行的时候,g2从g1里面取值,取不到值,就生成了一个空列表[]。
二、
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g=test()
for n in [1,10]:
g=(add(n,i) for i in g)
print(list(g))
解析:一步一步化简来看
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g=test()
n = 1
g = (add(n,i) for i in g)
n = 10
g = (add(n,i) for i in g)
print(list(g))
当n = 1时,并未进行输出,当第二次n = 10之后,才有最后的输出,n进行了两次赋值,后面的进行覆盖,覆盖前后,g都是未执行的(惰性),g前面做了定义,g每一次都是新生成的一个新的生成器,内存地址并不一样,所以需要带入,就有如下:
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g=test()
n = 10
g = (add(n,i) for i in (add(n,i) for i in g))
# g = (add(10,i) for i in (add(10,i) for i in g))
# g = (add(10,i) for i in (add(10,i) for i in test()))
print(list(g))
如果不太能理解,请看如下:
b = 10
b = 1
#赋值覆盖之前,下面的带入并未执行,模拟生成器的惰性
a = b
a = b+a
a = b+a+a
print(a)
————————————————————————————————————————————
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g=test()
print(g)
n = 1
g = (add(n,i) for i in g)
print(g)
n = 10
g = (add(n,i) for i in g)
print(g)
print(list(g))
pass
python学习日记(生成器函数进阶)的更多相关文章
- python学习日记(函数进阶)
命名空间 内置命名空间 存放了python解释器为我们提供的名字:print,input...等等,他们都是我们熟悉的,拿过来就可以用的方法. 内置的名字在启动解释器(程序运行前)的时候被加载在内存里 ...
- python学习日记(匿名函数)
匿名函数 简介 匿名函数:为了解决那些功能很简单的需求而设计的一句话函数. python 使用 lambda 来创建匿名函数. 所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数. lam ...
- python学习日记(函数--装饰器)
楔子 前提,我有一段代码(一个函数). import time def run_time(): time.sleep(0.1) print('我曾踏足山巅') 需求1:现在,我想计算这段代码的运行时间 ...
- Python学习日记(八) 函数
函数的结构: 函数的返回值: 1.当函数执行时运到return关键字将不再往下执行 def func(): print("hello world!") print("he ...
- Python之路----生成器函数进阶
def generator(): print(123) yield 1 print(456) yield 2 g = generator() ret = g.__next__() print('*** ...
- Python进阶-VI 生成器函数进阶、生成器表达式、推导式
一.生成器函数进阶 需求:求取移动平均数 1.应用场景之一,在奥运会气枪射击比赛中,每打完一发都会显示平均环数! def show_avg(): print('你已进入显示移动平均环数系统!') a ...
- Python 学习日记(第三周)
知识回顾 在上一周的学习里,我学习了一些学习Python的基础知识下面先简短的回顾一些: 1Python的版本和和安装 Python的版本主要有2.x和3.x两个版本这两个版本在语法等方面有一定的区别 ...
- Python学习日记 --day2
Python学习日记 --day2 1.格式化输出:% s d (%为占位符 s为字符串类型 d为数字类型) name = input('请输入姓名') age = int(input('请输入年龄 ...
- python学习日记(基础数据类型及其方法01)
数字 int 主要是用于计算的,常用的方法有一种 #既十进制数值用二进制表示时,最少使用的位数i = 3#3的ASCII为:0000 0011,即两位 s = i.bit_length() print ...
- python中的生成器函数是如何工作的?
以下内容基于python3.4 1. python中的普通函数是怎么运行的? 当一个python函数在执行时,它会在相应的python栈帧上运行,栈帧表示程序运行时函数调用栈中的某一帧.想要获得某个函 ...
随机推荐
- Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试
近来发现两个问题: 1.jenkins设置邮箱时邮件发送测试不成功,之前是成功的: 2.启动python服务时,使用8000端口报错,但是用其他端口可用: 百度了一下,原来是端口占用的问题,可用如下方 ...
- MySQL中关于数据类型指定宽度之后的情况
概述 MySQL有很多种数据类型,最常用的就是int,char,varchar,这些类型在创建表的时候都可以指定该字段的宽度,方法是在类型后面加一个括号,括号中写宽度就可以了. 但是,在指定宽度之后, ...
- Python3练习题求1000以内所有3和5的倍数的总和
sum = 0 for i in range(1,1000): if i%3 == 0 or i%5 == 0: sum += i print(sum)
- java.util.Collections.copy():列表List浅拷贝
今天同事问我怎样将一个列表(list1)拷贝到另一个列表(list2),然后修改新的列表(list2)不会影响到旧的列表(list1),想了一想,这是深拷贝啊. 可是,除了循环new还有别的办法吗,想 ...
- 网络编程--使用UDP发送接收数据
package com.zhangxueliang.udp; import java.io.IOException; import java.net.DatagramPacket; import ja ...
- 【学亮开讲】Oracle内外连接查询20181119
--内连接查询 --需求:查询显示业主编号.业主名称.业主类型名称 select os.id 业主编号,os.name 业主名称,ot.name 业主类型名称 from t_owners os,t_o ...
- keras中TimeDistributed的用法
TimeDistributed这个层还是比较难理解的.事实上通过这个层我们可以实现从二维像三维的过渡,甚至通过这个层的包装,我们可以实现图像分类视频分类的转化. 考虑一批32个样本,其中每个样本是一个 ...
- MySQL最大连接数设置
在使用MySQL数据库的时候,经常会遇到这么一个问题,就是“Can not connect to MySQL server. Too many connections”-mysql 1040错误,这是 ...
- WPF 将数据源绑定到TreeView控件出现界面卡死的情况
首先来谈一下实现将自定义的类TreeMode绑定到TreeView控件上的一个基本的思路,由于每一个节点都要包含很多自定义的一些属性信息,因此我们需要将该类TreeMode进行封装,TreeView的 ...
- vue 思維導圖
vue概念:vue是一個輕量級的javascript庫:是一種漸進式的框架:vue可以實現數據視圖雙向綁定. vue基礎語法:實例化.條件.循環 vue重頭戲:動畫.組件.過濾.ajax.自定義組件. ...