码迷,mamicode.com
首页 > 其他好文 > 详细

迭代器和生成器

时间:2021-03-29 11:55:10      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:ali   第一个   字典   eve   移动   return   参数   dir   break   

迭代器和生成器

一、迭代器

  • 什么是可迭代对象:可以被for循环的便是可迭代对象
可以通过下面方法来证明(也可以用该方法判断变量的数据类型)
li=[11,2,3,4,5]
dic={‘key1‘:‘a‘,‘key2‘:‘b‘}
print(isinstance(li,list))#True
print(isinstance(dic,dict))#True
#元组和集合就不一一证明了
num=12345
print(isinstance(num,Iterable))#False
这说明字典列表,元组,集合是可迭代对象,数字类型不是
  • 可迭代协议:可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法
可迭代的:内部必须含有一个__iter__方法
print(dir([1,2,3]))
print(dir(2,4,5))
print(dir{‘key‘:‘value‘})
print(dir{1,2,3})
#以上输出的结果中都含有__inter__方法
  • 迭代器:就是将存储数据和遍历数据的职责分离

迭代器优点:节约内存、不依赖索引取值、实现惰性计算

迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
两种迭代器
1.可迭代对象通过执行iter方法得到:使用__iter__()方法
li=[‘a‘,‘b‘,‘c‘,‘d‘]
iterator=li.__iter__() #将可迭代的转化成迭代器
item=iterator.__next__()# 取列表的第一个数据a
print(item)#输出:列表中的第一个数据a
item=iterator.__next__()# 取列表的第二个数据b
print(item)#输出:列表中的第二个数据b

2.调用方法直接返回:使用iter()返回迭代器本身next()返回容器的下一个元素
li=[‘a‘,‘b‘,‘c‘,‘d‘]
iterrator=iter(li) # 将可迭代的转化成迭代器
print(next(iterator))#a
print(next(iterator))#b
print(next(iterator))#c

如果不再有下一个元素的话程序会抛出异常,通过捕获异常来处理
li=[‘a‘,‘b‘,‘c‘,‘d‘]
iterrator=iter(li) # 将可迭代的转化成迭代器
while True:
    try:
        print(iterrator.__next__())
    except:
            break
#输出:
a
b
c
d

for循环的底层逻辑:
1.将可迭代对象转化成迭代器
2.内部使用__next__方法,一个一个取值
3.加了异常处理功能,取值到底后自动停止

二、生成器

能实现迭代器功能的就叫生成器,其本质是迭代器

  • 生成器函数:一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中获得返回值,但是yield又不同于return,return的执行意味着程序的结束,yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重新从它离开的地方继续执行。调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值,直到函数执行结束。
生成器自带了__iter__方法和__next__方法
def genrator():
    a=1
    print(‘现在定义的a变量‘)
    yield a
    b=2
    print(‘现在又定义了b变量‘)
    yield b
g=genrator()
print(g)#打印g可以发现g就是一个生成器
print(g.__next__())
print(‘--------‘)
print(g.__next__())
#输出:
<generator object genrator at 0x0154DDF0> 
现在定义的a变量
1
--------
现在又定义了b变量
2

也可以用next()来写
def genrator():
    a = 1
    print(‘现在定义了a变量‘)
    yield a
    b = 2
    print(‘现在又定义了b变量‘)
    yield b
g = genrator()
print(next(g))
print(next(g))
#输出:
现在定义了a变量
1
现在又定义了b变量
2

通过使用生成器,不会让内存中生成大量数据。

我们可以通过这个例子来理解一下:假如我想让工厂给生产面包,生产20000个面包,我和工厂一说,工厂应该是先答应下来,然后再去生产,我可以一个一个的要,也可以根据学生人数的多少,一批一批的拿。而不能是一说要生产20000个面包,工厂就先去做生产20000个面包,等把工厂把20000个做好了,前面生产的面包就已经过期了

def bread():
    for i in range(1,20000):
        yield "生产了%s个面包"%i
Bread=bread()
print(Bread.__next__())#要一个面包
print(Bread.__next__())#再要一个面包
print(Bread.__next__()) #再要一个面包
#要一批面包50件
num=0
for i in Bread:
    print(i)
    num++
    if num==50:
        break
  • 生成器函数进阶:send()方法
def generator():
    print(123)
    args=yield 1
    print(‘send传的参数是%s‘%args)
    print(456)
    yield 2
g=generator()
ret=g.__next__()
print(ret)
ret=g.send(‘聚焦教育‘)
print(ret)
#输出:
123
1
send传的参数是聚焦教育
456
2

send()方法说明:
1.send 获取下一个值的效果和next基本一致
2.只是在获取下一个值的时候,给上一yield的位置传递一个数据
使用send的注意事项:
1. 第一次使用生成器的时候 是用next获取下一个值
2. 最后一个yield不能接受外部的值

生成器函数应用实例:移动平均值
def mean():
    sum=0
    count=0
    val=0
    while True:
        num=yield val
        sum+=num
        count+=1
        val=sum/count
g=mean()
next(g)
ret=g.send(15)
print(ret)
ret=g.send(20)
print(ret)
  • 列表推导式
列表推导式
li=[i for i in range(10)]
print(li)#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
li=[‘选项%s‘%i for i in range(10)]
print(li)#[‘选项0‘, ‘选项1‘, ‘选项2‘, ‘选项3‘, ‘选项4‘, ‘选项5‘, ‘选项6‘, ‘选项7‘, ‘选项8‘, ‘选项9‘]

例一:10以内所有能被2整除的数
li=[i for i in range(10) if i%2==0]
print(li)#[0, 2, 4, 6, 8]

例二:20以内所有能被3整除的数的平方
def func(x):
    return x*x
ret=[func(i) for i in range(20) if i%3==0]
print(ret)#[0, 9, 36, 81, 144, 225, 324]

例三:找到嵌套列表中名字含有两个‘e’的所有名字
name_list = [‘Jim‘, ‘Boos‘, ‘Jeen‘, ‘Andrew‘, ‘Wesley‘, ‘Steven‘, ‘Joe‘]
ret=[i for i in name_list if i.count(‘e‘)>=2]
print(ret)#[‘Jeen‘, ‘Wesley‘, ‘Steven‘]

names = [[‘Jim‘, ‘Boos‘, ‘Jeen‘, ‘Andrew‘, ‘Wesley‘, ‘Steven‘, ‘Joe‘],
         [‘Alice‘, ‘Jill‘, ‘Ana‘, ‘Wendy‘, ‘Jennifer‘, ‘Sherry‘, ‘Tom‘]]
ret=[name for lst in names for name in lst if name.count(‘e‘)>=2]
print(ret)#[‘Jeen‘, ‘Wesley‘, ‘Steven‘, ‘Jennifer‘]

字典推导式
例一:将一个字典的key和value对调
dic = {‘a‘: 6, ‘b‘: 8}
ret={dic[k]:k for k in dic}
print(ret)#{6: ‘a‘, 8: ‘b‘}

例二:合并大小写对应的value值,将k统一成小写
dic = {‘a‘: 11, ‘b‘: 22, ‘A‘: 33, ‘Z‘: 44}
dic_ret={k.lower():dic.get(k.lower(),0)+dic.get(k.upper(),0)for k in dic.keys()}
print(dic_ret)
#{‘a‘: 44, ‘b‘: 22, ‘z‘: 44}

集合推导式
set1={x**2 for x in[1,2,3,4]}
print(set1)
#{16, 1, 4, 9}
  • 生成器表达式
大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议
例:计算一系列值的和
ret=sum(x**2 for x in range(3))
print(ret)#5

迭代器和生成器

标签:ali   第一个   字典   eve   移动   return   参数   dir   break   

原文地址:https://www.cnblogs.com/peng-blogs/p/14556603.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!