迭代器和生成器

迭代器

可以把迭代器比喻成一个容器,可以从这个容器中一个接一个的把值取出来,取值的过程就是可迭代的过程

  可迭代协议:含有__iter__方法

  迭代器协议:含有__iter__方法 且 __next__方法

  查看方法:print(l1.__dir__)


可迭代对象: 含有__iter__方法 

迭代器: 含有__iter__方法和__next__方法 迭代器.__next__ 调用迭代器


(迭代器 是 可迭代对象 的 子集)


第一种判断方法:

  判断是不是可迭代:print('__iter__' in dir(对象)) 对象可以是字符串、列表、字典、等等

  判断是不是迭代器:print('__next__' in dir(对象)) 

第二种判断方法:

from collections import Iterable

from collections import Iterator
print(isinstance(对象,Iterable))   # 判断是不是可迭代的
print(isinstance(对象,Iterator))   # 判断是不是迭代器

例如:

li = []
a = li.__iter__()
from collections import Iterator
from collections import Iterable
print(isinstance([1,2],Iterable))       #从功能上判断    而type()是类型  是就是  不是就不是
print(isinstance([1,2],Iterator))
print(isinstance(a,Iterator))
# True
# False
# True

迭代器的特点:
  从前往后依次去取值,不可逆,不可重复
  惰性运算
  节省内存

取值超出范围,会抛出一个异常:

li = [1,2,3,4,5]
li_iter = li.__iter__()
print(li_iter.__next__())
print(li_iter.__next__())
print(li_iter.__next__())
print(li_iter.__next__())
print(li_iter.__next__())
print(li_iter.__next__())
#Traceback (most recent call last):
#  File "D:/Demo/test2.py", line 26, in <module>
#    print(li_iter.__next__())
#StopIteration                                 超出5次  报错 StopIteration

while 模拟 for

li = [1,2,3,4,5]
li_iter = li.__iter__()
while True:
    try:
        print(li_iter.__next__())

    except StopIteration:          #StopIteration  报出异常
        break

python3内置迭代器:

    内置迭代器:range() file(文件for循环读) enumerate()

生成器

生成器的本质: 就是 迭代器 内含__next__()方法,

一个函数里面如果有yield,那么这个函数就是生成器函数

def ge_iter():              #ge_iter()  就是生成器函数           生成器函数  里面有    yield关键字
    print('aa')
    yield 11        # 见到yield 取一次值,且记住这个位置,下次直接从这里开始
    print('bb')
    yield 15
    yield 1
    yield 2
g = ge_iter()         # ge_iter()是生成器               生成器就是迭代器,能用__next__方法取值
print(g.__next__())
# aa
# 11

while取值:

def ge_iter():
    print('aa')
    yield 11
    print('bb')
    yield 15
    yield 1
    yield 2
g = ge_iter()           调用ge_iter()不会立即执行,而是返回一个生成器
while True:
    try:
        print(g.__next__())
    except StopIteration:
        break

for循环取值:

def ge_iter():
    for i in range(10):
        yield "我是第{}名".format(i)
g = ge_iter()
print(g.__next__())
print(g.__next__())
for i in range(3):                # for循环取一段值
    print(g.__next__())      # 会接着上次保存的位置,接着取值
# 我是第0名
# 我是第1名
# 我是第2名
# 我是第3名
# 我是第4名

生成器惰性特点:

def fuc1(n,i):
    return n + i
def duoxing():
    for i in range(4):
        yield i
g = duoxing()
for n in [1,10]:
    g = (fuc1(n,i) for i in g)    关键点:因为生成器不取值(不打印),就永远不去运算,不运算,但for不停,
                                            所以最后 g = (fuc1(n,i) for i in (fuc1(n,i) for i in duoxing()))   
print(list(g))                      直接把 n = 10 带入计算
# [20, 21, 22, 23]

生成器取值,取了就没了:

def fuc1(n,i):
    return n + i
def duoxing():
    for i in range(4):
        yield i
g = duoxing()
for n in [1,10]:
    g = (fuc1(n,i) for i in g)                                               
    print(list(g))                          第一次取值了,g生成器就没了            不管循环多少次,只有第一次有值,取空了,后面都是空列表

#[1, 2, 3, 4]
#[]

==

一个生成器如下:

def cloth():
    for i in range(100):
        yield ‘衣服%’%i
g = cloth()

取值:

for i in g: 
    print(i)

#等价于

 for i in range(100):
    print(g.__next__())  #循环触发打印100次

for 自动触发可迭代g内部next方法 想当于第二个手动触发

为什么用for 是因为 next() 和send()有可能遇到取完再取的报错

而for循环取完就停止了

示例:文件的监视,监听文件的变化

def tail():
    f = open('文件','r',encoding='utf-8')
    f.seek(0,2)      #把光标定位到文件末尾
    while True:
        line = f.readline()  #从光标的位置读
        if line:         #line如果有内容  True
            yield line
        import time
        time.sleep(0.1)
g = tail()
for i in g:
    print(i.strip())
##注意 文件输入一定要保存 要不然显示不出来

send()方法

__send__() 可以写成send()
__next__() 可以写成next()

def f1():
    a = 1
    b = yield a   #执行到yield a 停止  返回a = 1    ***    send传值,想当于把yield a 替换 , 变成b = 5 ,继续往下走
    yield b   #定位到yield b   又停止,返回 b = 5
g = f1()
print(next(g))
print(g.send(5))
#1
#5

send 想当于next的同时传参( send(None) == next() ) 把生成器里面yield及后面的值整体替换成传入的参数

send不能 用在第一个触发生成器

next + send 数量和 = yield 数量

预激活装饰器

正常情况,需要用next()方法取值一次,才能用send()方法

#每传一个数,求每次传入后的平均值
def average():
    sum = 0
    count = 0
    aver_age = None
    while True:
        count += 1
        a = yield aver_age
        sum += a
        aver_age = sum/count

g_aver = average()
print(g_aver.__next__())
print(g_aver.send(10))
print(g_aver.send(20))
print(g_aver.send(30))
#10
#15
#20

可以利用装饰器,把第一个next()方法放到装饰器里面

def init(func):
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g         #这个地方一定要返回一个生成器
    return inner
@init
def average():
    sum = 0
    count = 0
    aver_age = None
    while True:
        count += 1
        a = yield aver_age       #yield后面放什么都行,这个主要是只要一个yield,来返回aver_age 的值
        sum += a
        aver_age = sum/count

g_aver = average()       #得到生成器,并赋值给g_aver,这一步不能省略,如果写成print(average.send(10)),想当于每次都重新调用average函数
# print(g_aver.__next__())   #这一步放到装饰器函数里面,预激活
print(g_aver.send(10))
print(g_aver.send(20))
print(g_aver.send(30))

yield from   

代替for 循环

for i in a:

  yield i

for i in a:
    yield i

# 等价于

yield from a

示例:

a = ‘abcd’  依次拿到‘a''b''c''d'
def func():
    a = 'abcd'
    for i in a:    #这
        yield i     #两句
g = func()
print(list(g))
# ['a', 'b', 'c', 'd']

------

def func():
    a = 'abcd'
    yield from a   #这一句
g = func()
print(list(g))
# ['a', 'b', 'c', 'd']

 

posted @ 2018-11-27 10:31  金元  阅读(166)  评论(0编辑  收藏  举报