美文网首页
【python】迭代器&生成器

【python】迭代器&生成器

作者: 我写的BUG代码少 | 来源:发表于2020-04-23 10:17 被阅读0次

B站视频链接

目录

一. 迭代器

1.可迭代协议&迭代器协议(Iterable/Iterator)

  • 只要是能被for循环的数据类型就一定拥有iter方法
  • 【==可迭代协议(Iterable)==】只要含有iter方法的都是可迭代的(就可以被for循环)
    • 判断是否含有iter方法(是否可以被for循环)
      print('__iter__' in dir(dict))
      >>> True
      
  • for 循环先去找iter方法,找到后开始遍历,如果没找到就报错
    • for循环实际是在使用迭代器
    • 可以被for循环的东西
      • list
      • dict
      • str
      • set
      • tuple
      • f = open() #文件
      • range()
      • enumerate
  • (可迭代的东西).__iter__() >>> 迭代器
    • 一个列表执行了iter()之后的返回值就是一个迭代器
      [].__iter__() #迭代器
      
  • 迭代器包含·的方法有:
    #【1】__setstate__
    
    #【2】__next__    #通过next可以从迭代器中一个一个的取值
    
    #【3】__length_hint__
    [0,1].__iter__().__length_hint__() #元素个数
    >>> 2
    
  • 【==迭代器协议(Iterator)==】内部同时含有nextiter方法的就是迭代器
    from collections import Iterator
    
    class A:
        def __iter__(self): pass
        def __next__(self): pass
    
    a = A()
    print(isinstance(a, Iterator)) 
    >>> True
    
    • 模拟for循环
    list1 = [1,2,3,4]
    iterator = list1.__iter__()
    while True:
        print(iterator.__next__())
    

2.迭代器的好处

  • 从容器类型中一个一个的取值,会把所有值都取到
  • 节省内存空间
    • 迭代器并不会在内存中再占用一大块内存
      • 而是随着循环 每次生成一个
      • 每次next每次给我一个
    range(10000000000)  #类似定义,但还未读取占空间
    print(list(range(10000000000)))
    

二. 生成器(自己写的迭代器)

1.生成器函数(自己写的函数)

  • 含有yield关键字的函数,是生成器函数
  • 特点:
    • 调用函数之后,函数不执行,返回一个生成器
    • 每次调用next方法的时候会取到一个值
    • 直到取完最后一个,再执行next会报错
  • yield不能与return共用,且需要写在函数内部
    • return会会结束
    • yelid后不会结束
def generator():  #1.定义生成器函数
    print(1)
    yield 'a'

ret = generator() #2.生成器函数调用generator(),并得到返回值(返回值ret是一个生成器) #3.返回的生成器 >>> ret
print(ret)   #>>> <generator object generator at 0x000001BC7CC2A5C8> 说明内部代码未被执行,ret是生成器 
print(ret.__next__()) #ret是生成器(迭代器)含有next方法,可调用__next__()

>>>
1
a

[图片上传失败...(image-eb4702-1587608216353)]

  • 生成器可阶段性取值(每做一部分就可以取值)
def func():
    for i in range(2000000):
        yield '取值%s'%i

g = func()  #生成器1
g1 = func()  #生成器2(新生成器,与生成器1无关)

#从生成器1取50个
count = 0
for i in g:
    count+=1
    print(i)
    if count >50:
        break

#从生成器2继续取50个(共取100个)
for i in g:
    count+=1
    print(i)
    if count >100:
        break

监听文件输入的例子

def tail(filename):
    f = open(filename, encoding='utf-8')
    while True:
        line = f.readline()
        if line.strip(): #如果line.strip()[去掉回车]不为空
            yield line.strip() #不能用return,用return只能取到第一行
            
g = tail('file')
for i in g:
    if 'python' in i:  #监听过滤/有‘python’就打印,没有就不打印
        print(i)

send()的用法

  • send() 获取下一个值的效果和next基本一致
  • 只是再获取下一个值的时候,给上一个yield的位置传一个数据
  • 使用send的注意事项
    • 第一次不能用send
    • 第一次使用生成器的时候,是用next获取下一个
    • 函数中最后一个yield不能接收新的值
def generator():
    print(123)
    content = yield 1
    print('=====',content)
    print(456)
    yield 2

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

>>>
123
*** 1
===== hello
456
*** 2

[图片上传失败...(image-cb1cf0-1587608216353)]

  • 函数执行:先执行等号右边,再执行左边
  • 执行第一个next的时候,先执行到等号右边的yield,此时还没有赋值给content;下一步send的值发送给yield并赋值给等号左边的content

如果yield后还有要执行的代码

def generator():
    print(123)
    content = yield 1
    print('=====',content)
    print(456)
    arg = yield 2
    ''' 代码语句'''
    yield    #这里yield空


g = generator()
ret = g.__next__()
print('***',ret)
ret = g.send('hello') 
print('***',ret)

获取移动平均值

def average():
    sum = 0
    count = 0
    avg = 0
    while True:
        num = yield avg
        sum += num
        count += 1
        avg = sum/count
        

avg_g = average()
avg_g.__next__()
avg1 = avg_g.send(10)
avg1 = avg_g.send(20)
'''......'''
print(avg1)
预激生成器的装饰器
def init(func):
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        g.__next__()
        return g
    return inner

@init
def average():
    sum = 0
    count = 0
    avg = 0
    while True:
        num = yield avg
        sum += num
        count += 1
        avg = sum/count
        

avg_g = average()
avg1 = avg_g.send(10)
avg1 = avg_g.send(20)
print(avg1)

[图片上传失败...(image-7007f9-1587608216353)]

yield from

  • 原代码
def generator():
    a = 'abchdhb'
    b = '123555'
    for i in a:
        yield i
    for i in b:
        yield i

g = generator()
for i in g:
    print(i)
  • 修改后
def generator():
    a = 'abchdhb'
    b = '123555'
    yield from a
    yield from b

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

2.生成器表达式

#【生成器表达式】
g = (i for i in range(10)) #g是生成器
for i in g:
    print(i)

#【列表表达式】
list = [i for i in range(10)]
for i in list:
    print(i)
  • 区别:
    • 括号不一样
    • 返回的值不一样(生成器表达式几乎不占用内存)

面试题

  • 1.生成器中的数据只能取一次,取完就没有了
  • 2.惰性运算:不找它取值,它就不工作

面试题1

def demo():
    for i in range(4):
        yield i

g = demo()

g1 = (i for i in g)
g2 = (i for i in g1)

print(list(g1))
print(list(g2)) #g1的值已经被取完了
>>>
[0, 1, 2, 3]
[]

面试题2: for循环+生成器

def add(n,i):
    return n+i

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

g = test()

for n in [1,10]:
    g = (add(n,i) for i in g)
'''
n = 1
g = (add(n,i) for i in g) #1
n = 10
g = (add(n,i) for i in g)  #都未执行,直到print语句才执行,这个时候再找g
>>>
n = 1
g = (add(n,i) for i in g)
n = 10
g = (add(n,i) for i in (add(n,i) for i in g)) #这步的g是1.的g
>>>
n = 1
g = (add(n,i) for i in g)
n = 10
g = (add(n,i) for i in (add(n,i) for i in test()))
>>>
n = 1
g = (add(n,i) for i in g)
n = 10
g = (add(n,i) for i in (add(10,i) for i in test())) #i=0,1,2,3
#g = (add(n,i) for i in (10,11,12,13))
#g = (add(10,i) for i in (10,11,12,13))
'''

print(list(g))
>>>
[20, 21, 22, 23]
def add(n,i):
    return n+i

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

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

识别多个文件中的特殊字段,并返回文件名

  • 一个文件夹下,有多个文件,找寻含有'python'字段的文件,并返回文件名
import os

def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

@init
def list_files(target):
    while 1:
        dir_to_search=yield
        for top_dir,dir,files in os.walk(dir_to_search):
            for file in files:
                target.send(os.path.join(top_dir,file))
@init
def opener(target):
    while 1:
        file=yield
        fn=open(file)
        target.send((file,fn))
@init
def cat(target):
    while 1:
        file,fn=yield
        for line in fn:
            target.send((file,line))

@init
def grep(pattern,target):
    while 1:
        file,line=yield
        if pattern in line:
            target.send(file)
@init
def printer():
    while 1:
        file=yield
        if file:
            print(file)

g=list_files(opener(cat(grep('python',printer()))))

g.send('/test1')

协程应用:grep -rl /dir

相关文章

网友评论

      本文标题:【python】迭代器&生成器

      本文链接:https://www.haomeiwen.com/subject/ugkoihtx.html