迭代器和生成器

 函数例子--迭代器--员工信息表

迭代器

能被for循环的数据类型
list  # 列表
dict  # 字典
str # 字符
set  # 集合
tuple # 元组
f = open()  # 文件句柄
range()
enumerate  # 枚举

查看数据类型含有什么方法  使用dir()

print(dir([]))

双下方法:__方法名__()

print(dir([])) 查看方法
print('__add__' in dir([])) #判断双下方法__add__是否在列表方法内
print([1].__add__([2]))  # 等同于print([1]+[2])
print([1] + [2])  # 实际运行的方法是print([1].__add__([2]))

 可迭代对象
只要数据类型含有__iter__方法的的数据类型都是可迭代对象
只要都能被for循环的都是可迭代对象

迭代器
当可迭代对象使用了__iter__方法后,它的返回值就是一个迭代器
只要是迭代器 一定可迭代

print(dir([]))
print(dir([].__iter__()))

迭代器必须同时包含__iter__和__next__两个方法
__next__()方法可以一个一个的获取值 # for循环其实就是在使用迭代器

print(set(dir([].__iter__())) - set(dir([])))
# 例子
l = [1, 2, 3]
print(l.__iter__().__next__())  # 将l 列表通过__iter__()转为迭代器,接着通过__next__可以取一个值
print(l.__iter__().__next__())  # 将l 列表通过__iter__()转为迭代器,接着通过__next__可以取下一个值
print(l.__iter__().__next__())  # 将l 列表通过__iter__()转为迭代器,接着通过__next__可以取下一个值
print(l.__iter__().__next__())  # 因列表已经没有第四个值,直接报错

# 模拟for循环
l = [1,2,3,4,5,6,7,8,9]
a = iter(l) # 将l 转为为迭代器
print('__iter__'and '__next__' in dir(a))
while True:
    print(a.__next__())

可迭代协议只要含有__iter__方法的都是可迭代的
迭代器协议内部含有__iter__和__next__方法的迭代器

迭代器的好处
    1、从容器类型中一个一个的取值,会把所有的值都取到;
    2、节省内存空间 :不会一次性产生所有值,仅通过循环或__next__(),一次取一个

print(range(100000000))
print(list(range(100000000)))

 注意:迭代器很好,但是并不能解决所有的,比如我需要生成200W个不同的字符串,于是引出生成器

生成器

生成器的本质就是一个迭代器

生成器函数

1、只要含有yield关键字的函数都是生成器函数,只能用在函数内且不能和return同时存在

2、且执行完yield后这个函数不会结束,可以用__next__接着取值 

3、调用函数的时候,函数体不执行,返回一个生成器

4、调用next方法的是会取到一个值,直到完全取完,再执行__next__()就会报错

def get():
    for i in range(200):
        yield i

ret = get()
print(ret)
print('__iter__'and '__next__' in dir(ret))
while True:
  print(ret.__next__())
#for i in ret: # print(i)

生成器函数的取值方式

1、__next__()

2、for

3、数据类型的强制转换 list(ret) 占用内存

注意:因为yield关键字,返回值后函数并没有结束,所以可以接着往下用__next__()取值,直到报错

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
    print(3)
    yield 'c'

g = generator()
ret = g.__next__()
print(ret)
ret = g.__next__()
print(ret)
ret = g.__next__()
print(ret)
ret = g.__next__()  # 会报错 因为只有3个yield
print(ret)

#for i in g:
#    print(i)

 注意:解决---我需要生成200W个不同的字符串

def test():
    for i in range(200):
        yield '第%s个 Hello'%i

g = test()
for i in g:
    print(i)

问题的进阶:仅需要前50个,接着取51个能否取到?

def test():
    for i in range(200):
        yield '第%s个 Hello'%i

g = test()
count = 0
for i in g:
    count += 1
    print(i)
    if count >50:
        break
print(g.__next__())

注意:每对新的变量赋予一次生成器函数调用,不于之前的有任何关联

def test():
    for i in range(200):
        yield '第%s个 Hello'%i

g = test()
g1 = test()
count = 0
for i in g:
    count += 1
    print(i)
    if count >50:
        break
print(g.__next__())
print(g1.__next__())

例子:监听文件输入

# 注意文件里输入内容后,保存一下,不然不会实时输出

def genternor(filename):
    with open(filename,encoding='utf-8') as f :
        while True:
            line = f.readline().strip()
            if line:
                print(line)

genternor('count')

问题进阶:如发现输入有Python,实时输出内容+‘******’,或者仅输出带Python的字符串

达到监听过滤的效果

def genternor(filename):
    with open(filename,encoding='utf-8') as f :
        while True:
            line = f.readline().strip()
            if line:
                yield line

ret = genternor('count')
for i in ret:
    if 'python' in i :
        print('*****',i)
    else:
        print(i)

#仅打印带python的字符串
# for i in ret:
#     if 'python' in i :
#         print(i)

 

生成器的进阶----send()方法

def gennerator():
    print(123)
    yield 1
    print(456)
    yield 2
    print(789)

g = gennerator()
ret = g.__next__()
print('****',ret)
# ret = g.__next__()
# print('****',ret)
ret = g.send(None)  # send的效果和next一样
print('****',ret)

send()方法:格式:send(数据)
send 获取下一个值的效果和__next__()基本一致,只是在获取下一个值的时候,给上一个yield的位置传一个数据

注意:使用send之前,必须先使用__next__()获取第一个yield的值,且注意最后一个yield不能接收外部的值

def gennerator():
    print(123)
    content = yield 1
    print('---',content)
    print(456)
    yield 2
    print(789)

g = gennerator()  # 调用函数,变量到一个生成器
ret = g.__next__()  # 执行__next__()方法,执行到yield 1 就直接返回了,并没有给content进行赋值
print('****',ret)  # 所以这里仅打印123 ,1
# ret = g.__next__()
# print('****',ret)
ret = g.send('数据')  # sent 开始从__next__()停留的位置开始接着执行,将括号内的数据传到停留的位置,接着对content赋值content=‘数据’
print('****',ret) # 接着执行到yield 2 后停止

 

 生成器例子:移动计算平均值>>带装饰器的生产器

 

 yield from

在一个容器类型里取值,不用一个个返回,用yield from可有一次性返回

def generator():
    a = 'abcdef'
    b = '123456'
    yield from a
    yield from b
g = generator()
for i in g:
    print(i)

 

 

 

 

posted on 2019-02-14 17:35  Jerry-Wang  阅读(117)  评论(0)    收藏  举报