迭代器和生成器(要写的)

迭代器

可迭代对象:

  字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。

两种方式证明可迭代对象:

# 1 - 使用方法看对象有没有__iter__() 方法
ls = [1,2,3]
print(dir(ls)) #使用dir() 查看对象都有什么方法

# 2 - 使用模块
from collections import Iterable
# isinstance()判断哪个对象是什么类型
print(isinstance(ls,Iterable)) # True

可迭代协议

  可以被迭代要满足的要求就是可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。

总结:

  可以被for循环的都是可迭代的。

  可迭代的:内部必须含有一个__iter__方法

迭代器(iterator)

l = [1,2,3,4]
l_iter = l.__iter__() #将可迭代的转化为迭代器
item = l_iter.__next__() #取出第一个值
print(item) # 1
item = l_iter.__next__() #取出第二个值
print(item) # 2

迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法

for 循环内部:

  将可迭代对象转化为迭代器(可迭代对象.__iter__())

  内部使用__next__()方法,依次取值

  加了异常处理功能,取值到底后自动停止。

扫描二维码关注公众号,回复: 1654640 查看本文章

用while循环模拟for循环:

# 用while循环模拟for循环
l = [1,2,3,4]
i_iter = l.__iter__()
while True:
    try:
        item = i_iter.__next__()
        print(item)
    except StopIteration:
        break

总结:

  for 循环就是基于迭代器协议提供了一个统一的可遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就可以通过for循环来遍历了。

  最重要的一点,转化成迭代器,在循环时,同一时刻在内存中只出现一条数据,极大限度的节省了内存。

生成器

初始生成器(Generator)

  我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处就是可以节省内存。

  如果在某些情况下,需要自己写,我们自己写的这个能实现迭代器功能的东西就叫生成器。

本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)

特点:惰性运算,开发者自定义

Python中提供的生成器:

  1、生成器函数:常规函数定义,但是,使用yield返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

  2、生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。

生成器函数

  一个包含yield关键字的函数就是一个生成器函数。

  yield可以为我们从函数中返回值,

  调用生成器函数不会得到返回的具体的值,而是一个可迭代对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值,指导函数执行结束。

#生成器函数
def gene_func():
    yield 1
    yield 2
    yield 3
# print(gene_func()) # 不会执行函数 得到一个可迭代对象 <generator object gene_func at 0x1040fbba0>
ge_object = gene_func()
a = ge_object.__next__()
print(a) # 1
print(next(ge_object)) # 2

生成器:不会在内存中一下子生成太多数据。要多少就执行多少__next__()

Send方法

send()方法在获取下一个值的时候,给上一个yield的位置传递一个参数

def gene_func():
    send_fc1 = yield 1
    print('send_fc1 : %s'%send_fc1) #赋值
    yield 2
ge_obj = gene_func()
ret = ge_obj.__next__() # 1
print('第一个yield返回的值:%s'%ret)
ret1 = ge_obj.send('赋值') # 2
print('send赋值:%s'%ret1)

  send获取下一个值的效果和next基本一致,只是在获取下一个值得时候,给上一个yield的位置传递一个数据。

注意:

  第一次使用生成器的时候,因为没有上一个yield,用next获取下一个值

  最后一个yield不能接受外部的值。

def add(n, a_i):
    return n + 1

def test():
    for r_i in range(4):
        yield r_i

g = test()
for n in [1,10]:
    g = (add(n, i) for i in g) #

# 第一次: g = ((add(n,i),add(n,i),add(n,i),add(n,i)) 没有调用,不执行 n不赋值
# 第二次:g = (add(n,add(n,i)),add(n,add(n,i)),add(n,add(n,i)),add(n,add(n,i)))

print(list(g))

'''
每个生成器只能取一次 只能前进不能后退
生成器在不找他要值得时候始终不执行
当他执行的时候,要以执行时候的所有变量为准
'''

猜你喜欢

转载自www.cnblogs.com/liangying666/p/9202050.html
今日推荐