迭代器和生成器

******迭代器******

迭代:能将某个数据集合内的数据一个接一个的取出来

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

比如

复制代码
from collections import Iterable
                             
l = [1,2,3,4]                
t = (1,2,3,4)                
d = {1:2,3:4}                
s = {1,2,3,4}                
                             
print(isinstance(l,Iterable))  #True
print(isinstance(t,Iterable))  #True
print(isinstance(d,Iterable))  #True  
print(isinstance(s,Iterable))  #True
复制代码

可迭代协议

内部含有"__iter__"方法的数据类型,就是可迭代的----------可迭代协议

ret = 'abc'.__iter__()
print(ret)
结果:<str_iterator object at 0x0000029ADB757908>

一组可迭代的数据类型经过一个.__iter__()方法,就形成了一个迭代器iterator

迭代器你可以理解为一个容器,我们从这个容器当中一个接着一个的把值取出来的过程就是迭代的过程
列表和列表的迭代器的方法区别:
print(set(dir([1,2].__iter__()))-set(dir([1,2])))

结果:
{'__length_hint__', '__next__', '__setstate__'}

上面结果中的三个方法就是迭代器比原数据类型多的用法,其中主要用"__next__",它的作用是从迭代器中一个一个取值

其实在for循环中,就是内部调用了"__next__"方法才能取到一个一个值

复制代码
l = [1,2,3,4]
l_iter = l.__iter__()
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
复制代码

上面的程序报错,因为已经超出了迭代器能取得范围

可以采取异常处理机制来把这个异常处理掉。

复制代码
l = [1,2,3,4]
l_iter = l.__iter__()
while True:
    try:
        item = l_iter.__next__()
        print(item)
    except StopIteration:
        break
复制代码

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

迭代器的用法

比如lst是一个列表

lst_iter.__next__()就是用它的迭代器把它的元素一个一个取出来

小结:

可迭代的 必须含有 __iter__方法---->可迭代协议
迭代器比可迭代的多一个 __next__方法
迭代器:包含__next__,__iter__方法---->迭代器协议
包含__next__方法的可迭代对象就是迭代器
迭代器是可迭代的 的一部分
获得迭代器:可迭代的调用 __iter__()
使用迭代器:迭代器.__next__()
如何判断一个对象是不是迭代器或者可迭代对象?
1,比如range()是不是可迭代器或者可迭代对象
1,判断方法是否在对象里
2,isinstance(对象,Iterator or Iterable)

复制代码
print('__next__' in dir(range(12)))  #查看'__next__'是不是在range()方法执行之后内部是否有__next__
print('__iter__' in dir(range(12)))  #查看'__next__'是不是在range()方法执行之后内部是否有__next__

from collections import Iterator
print(isinstance(range(100000000),Iterator))  #验证range执行之后得到的结果不是一个迭代器

##结果False,True,False,所以它是可迭代的,但不是一个迭代器
复制代码
迭代器的工作原理
l_iter = [1,2,3,4,5].__iter__()
只是记录当前这个元素和下一个元素
迭代器的特点,要一个给一个(惰性运算),从前到后依次取值,过程不可逆,不可重复,节省内存。
迭代器是个好东西
目前我们已知的可迭代的都是python提供给我们的比如
range()
enumerate()
在平时写代码的过程中,我们用for循环就够了
******生成器******
我自己想写个可迭代的,——生成器
生成器的本质就是迭代器
因此生成器的所有好处都和迭代器一样
但是 生成器是我们自己写的python代码
生成器的方法
1,生成器函数
def g_func():
    yield 1

g = g_func()
print(g)

生成器Generator:

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

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

Python中提供的生成器:

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

一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。

生成器函数中含有yeild关键字

生成器函数调用时不会立即执行,而是返回一个生成器

复制代码
def g_func():
    yield 1

g = g_func()
print(g)

结果生成一个生成器
复制代码

 举例

复制代码
import time
def genrator_fun1():
a = 1
print('现在定义了a变量')
yield a
b = 2
print('现在又定义了b变量')
yield b

g1 = genrator_fun1()
print('g1 : ',g1) #打印g1可以发现g1就是一个生成器
print(g1.__next__())
print(g1.__next__())
结果

  g1 : <generator object genrator_fun1 at 0x0000020FC9F31E08>
  现在定义了a变量
  1
  现在又定义了b变量
  2


复制代码

生成器有什么好处就是不会一下子在内存中生成太多数据

假如我想让工厂给学生做校服,生产2000000件衣服,我和工厂一说,工厂应该是先答应下来,然后再去生产,我可以一件一件的要,也可以根据学生一批一批的找工厂拿。

复制代码
def produce():
    for i in range(2000000):
        yield "第%s件衣服"%i

product_g = produce()
print(product_g.__next__()) 
print(product_g.__next__())
print(product_g.__next__()) 
num = 0
for i in product_g:         
    print(i)
    num +=1
    if num == 5:
        break
复制代码

 监听文件末尾添加信息

复制代码
def tail():
    f = open('文件','r',encoding='utf-8')
    f.seek(0,2)
    while True:
        line = f.readline()
        if line:
            yield line
        import time
        time.sleep(0.1)
g = tail()
for i in g:
    print(i.strip())
复制代码

求平均值

复制代码
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count

g_avg = averager()
g_avg.__next__()
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(20))
print(g_avg.send(100))
print(g_avg.send(200))
结果是:

  10.0
  20.0
  20.0
  40.0
  72.0

复制代码

send

从哪一个yield开始接着执行,就把一个值传给了那个yield
send不能用在第一个触发生成器
生成器函数中有多少个yield就必须有多少个next+send
复制代码
def func():
    print('*'*10)
    a = yield 5
    print('a : ',a)
    yield 10
g = func()
num = g.__next__()
num2 = g.send('alex')
print(num)
print(num2)

结果:

  **********
  a : alex
  5
  10

复制代码

生成器的预激装饰器


复制代码
def init(func):  #生成器的预激装饰器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)   #func = averager
        g.__next__()
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    term = yield average
    total += term
    count += 1
    average = total/count
    yield average

g_avg = averager()
print(g_avg.send(10))
print(g_avg.send(30))
复制代码

yield from

复制代码
def func():
    a = 'AB'
    b = 'CD'
    yield from a
    # for i in a:yield i
    yield from b
    # for i in b:yield i

'A','B','C','D'
#返回了4次
g = func()
for i in g:
    print(i)
复制代码
g.__next__()==>next(g)
g.__iter__()==>iter(g)

小结:
#生成器函数:生成一个生成器的函数
#生成器的本质参数迭代器
#生成器函数的特点:
# 带有yield关键字
# 且调用之后,函数内的代码不执行

#触发执行的方式:
#next
#send :send(None) == __next__(),send在next的基础上传一个值到生成器函数内部
#send操作不能用在生成器使用的第一次
#for循环
原文地址:https://www.cnblogs.com/xyhh/p/10833595.html