迭代器和生成器函数

引入:

l = [1,2,3,4,5]
s = {1,2,3,4}
for i in l:
    print(i)

如果代码是:

for i in 50:
    print(i)

这个运行不了。输出结果是 'int' object is not iterable

iterable是可迭代的意思。

哪些可以迭代呢?这些可以str、list、tuple、set、dic等

可迭代的标志是  _iter_。

那我们如何判断是否可以迭代呢?

print('__iter__' in dir([1,2,3]))

双下划线的内置方法一般不用这种方法调用:

print(next(l.__iter__()))

而是用这种方法调用:

print(next(iter(l)) )

小结:

(1)可迭代协议——凡是可迭代的内部都有一个__iter__方法

(2)迭代器里既有iter方法,又有next方法 ——迭代器协议

(3)通过iter(o)得到的结果就是一个迭代器

(4)0是可迭代的对象

判断是否是迭代器和可迭代对象的方法:

from collections import Iterable
from collections import Iterator
s = 'abc'
print(isinstance(s,Iterable))
print(isinstance(s,Iterator))
print(isinstance(iter(s),Iterator))

生成器包括两个方面:生成器函数和生成器表达式

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

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

python中内置的生产器:

(1)生成器函数:关键字是yield,yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次继续运行。

(2)生成器表达式:类似于列表推导式,但是返回的是一个对象而不是一个结果列表 ,即惰性运算。

生成器函数:一个含有yield的函数就是生产器函数。通过调用返回的是一个对象,而不是结果列表。通过每一次对这个对象的请求就得到一个值def func():    print('aaaa')

    a=1
    yield a#返回第一个值
    print('bbbbb')
    yield 12#返回第二个值
    print('cccccc')
    yield 56#返回第三个值
ret=func()#拿到一个生成器
print(next(ret))#取第一个值
print(next(ret))#ret不可以用func替代
print(next(ret))

生成器例子:可以监听一个文本输入的文本

import time
def tail(filename):
    with open(filename) as f:
        f.seek(0,2)
        while True:
            line=f.readline()
            if not line:
                #time.sleep()
                continue
            yield line
for line in tail('监视文件'):
    print(line,end='')

可以计算移动平均值的例子:

def averager():
    total=0
    day=0
    average=0
    while True:
        day_num=yield average
        total+=day_num
        day+=1
        average=total/day
avg=averager()#直接返回生成器
next(avg)#激活生成器avg.sed(),什么都 不send和next效果一样
print(avg.send(10))
print(avg.send(15))
print(avg.send(80))

装饰器版计算移动平均值的例子:

def wrapper(func):
    def inner(*arge,**kwargs):
        ret=func(*arge,**kwargs)
        next(ret)
        return ret
    return inner
@wrapper
def averager():
    total=0
    day=0
    average=0
    while True:
        day_num=yield average
        total+=day_num
        day+=1
        average=total/day
avg=averager()#直接返回生成器
# next(avg)#激活生成器avg.sed(),什么都 不send和next效果一样
print(avg.send(10))
print(avg.send(15))
print(avg.send(80))

列表推导式和生成器表达式:

列表推导式:(两个代码实现相同的效果)

for i in range(100):
    print(i*i)
l=[i*i for i in range(100)]
print(l)
l = [{'name':'v','age':28},{'name':'v'}]
name_list = [dic['name'] for dic in l]
print(name_list)

生成器表达式:

l=(i*i for i in range(100))
print(next(l))
print(next(l))
print(next(l))
print(next(l))
l = [{'name':'v1','age':28},{'name':'v2'}]
name_list_generator = (dic['name'] for dic in l)
#print(name_list_generator)
print(next(name_list_generator))
print(next(name_list_generator))

我们会发现列表推导式和生成器表达式除了括号不一样外都一样。列表推导式是中括号,生成器表达式是小括号。

区别:列表推导式是把所有的数据都一起算出来,而生成器表达式是一种惰性运算你请求一次给你一个数据。因此相比较来说生成器表达式更节省内存。

Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

print(sum(x**2 for x in range(3)))

而不用多此一举先构造一个列表啦

print(sum([x**2 for x in range(3)]))

总结:

可迭代对象:

 拥有_inter_方法

 惰性计算

像str、list、tuple、set、dic等

迭代器:

拥有_inter_方法和next方法

像:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o

特点: 可以用for循环、 可以节省内存、只能用一次

生成器:本质是迭代器(同时拥有_inter_和next方法)

是开发人员自己定义的迭代器

优点:

1.延迟计算,你请求一次他给你返回一个结果。他不会给你返回所有的结果,不会大量占据内存

2.代码简洁,容易读

原文地址:https://www.cnblogs.com/1a2a/p/7275501.html