zoukankan      html  css  js  c++  java
  • 生成器和迭代器

    迭代器

    迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

    1 特点

    1. 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
    2. 不能随机访问集合中的某个值 ,只能从头到尾依次访问
    3. 访问到一半时不能往回退
    4. 便于循环比较大的数据集合,节省内存

    2 迭代器的概念

    迭代器协议:内部含有__next____iter__方法的就是迭代器

    可迭代对象:内部有__iter__方法就是可迭代对象。还有__next__的话就是迭代器

    迭代器协议和可迭代协议:

    1. 可以被for循环的都是可迭代的
    2. 可迭代的内部都有__iter__方法
    3. 只要是迭代器,一定是可迭代的
    4. 可迭代的.__iter__()就可以得到一个迭代器
    5. 迭代器的.__next__方法可以一个一个的获取值
    l = [1,2,3]  # l 可迭代对象
    print('__iter__' in dir(l)) # True
    print('__next__' in dir(l)) # False 
    l = iter(l) # 可迭代对象通过调用iter()方法就能得到一个迭代器
    print(l) # <list_iterator object at 0x0579DD30>
    print('__iter__' in dir(l)) # True
    print('__next__' in dir(l)) # True
    

    for循环其实就是在使用迭代器。每循环一次,调用一次__next__方法

    只有是可迭代对象才能使用for循环。当我们遇到一个新的变量,如果无法确定是否可以使用for,可以查看这个变量是否有__iter__方法,有就是可迭代的,可以使用for。

    Iterator,可迭代对象,直接给出对象对应的内存地址。

    3 迭代器的好处

    1. 从容器类型中取值,会把所有值都取到。
    2. 节省内存空间。 迭代器并不会在内存中再占用一大块内存,而是随着循环,每次生成一个(每次next就给一个)

    生成器

    一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器;

    生成器本质就是 迭代器。

    def func():
        yield 1
        yield 2    
        yield 3    
        yield 4
    

    上述代码中:func函数称为生成器,当执行此函数func()时会得到一个迭代器。

    >>> temp = func()
    >>> temp.__next__()1
    >>> temp.__next__()2
    >>> temp.__next__()3
    >>> temp.__next__()4
    >>> temp.__next__()Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration
    
    从生成器中取值的几个方法:
    	next(生成器)
    	for循环
    	数据类型的强制转换 : 占用内存
    

    1 生成器的表现形式

    1 生成器函数

    本质上就是我们自己写的函数。只要含有yield关键词的函数都是生成器函数。

    # yield不能和return共用且需要写在函数内
    l = [1,2,3,4,5]
    
    def generator():
        print(1)
        yield 'a'
        
    ret = generator() # ret 是执行之后得到的生成器
    print(ret) # 打印的是生成器对象 <generator object generator at 0x04FCABB0>
    print(ret.__next__())# 可以使用__next__方法
    print(dir(ret))
    #['__class__', '__del__', '__delattr__', '__dir__', '__doc__',
     '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
     '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__',
     '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__',
     '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
     '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running',
     'gi_yieldfrom', 'send', 'throw']
    

    执行之后会得到一个生成器作为返回值

    # 不同的生成器之间互不影响。
    def wahaha():
        for i in range(2000000):
            yield '娃哈哈%s'%i
    g = wahaha()
    g1 = wahaha()
    print(g.__next__()) # 0
    print(g1.__next__())# 0
    

    特点

    1. 调用函数的之后函数不执行,返回一个生成器。

    2. 每次调用next方法的时候会取到一个值。

    3. 直到取完最后一个,在执行next会报错 StopIteration

    2 生成器表达式

    g = (i for i in range(10)) # 注意:括号必须是()才返回生成器,括号不一样返回不同的值。几乎不占用内存
    print(g)
    for i in  g:
        print(i)
    

    2 监听文件输入的案例

    # 内容中有检索的字符,就输出对应行
    def tail(filename):
        f = open(filename,encoding='utf-8')
        while True:
            line = f.readline()
            if line.strip():
                yield line.strip()
    
    g = tail('file')
    for i in g:
        if 'python' in i:
            print('***',i)
    

    3 案例

    # 生成器实现:有一个文件,从文件里分段读取内容,在读出来的内容前面加上一个'***',再返回给调用者
    def readfile(filename):
        f = open(filename,encoding='utf8')
        while True:
            content = f.readline()
            if content:
                yield '***'+content
    
    g = readfile('file')
    for i in  g:
        print(i)
    

    生成器函数进阶

    send 获取下一个值的效果和next基本一致。只是在获取下一个值的时候,会给上个yield的位置传递一个数据

    1 使用send的注意事项

    ​ 第一次使用生成器的时候 是用next获取下一个值,最后一个yield不能接受外部的值

    def generator():
        print(123)
        content = yield 1 
        print('=======',content)
        print(456)
        arg = yield 2
        ''''''
        yield
    # g1 = generator()
    # g1.__next__()
    # print('***',generator().__next__())
    
    g = generator() #得到生成器
    ret = g.__next__() # 打印123,执行第一个yield
    print('***',ret) # *** 1
    ret = g.send('hello')   #send的效果和next一样,只是在获取下一个值的时候,会给上个yield的位置传递一个数据,此时 content = 'hello',继续向后运行,打印content,456
    print('***',ret) # *** 2
    

    2 获取平均移动值的案例

    # 获取移动平均值
    # 10 20 30 10
    # 10 15 20 17.5
    # avg = sum/count
    def average():
        sum = 0
        count = 0
        avg = 0
        while True:
            num = yield avg
            sum += num    # 10
            count += 1    # 1
            avg = sum/count
    
    avg_g = average() # 得到生成器
    avg_g.__next__() # 此时执行第一个next方法,返回avg的值0
    avg1 = avg_g.send(10)# 传递一个10给num,继续往后执行,此时num=10,sum=0+10,count=0+1,avg=10,循环继续,执行下一个yield。
    avg1 = avg_g.send(20)# 再次传递一个20给num,继续往后执行,此时num=20,sum=10+20=30,count=1+1=2,avg=15,循环继续,执行下一个yield
    print(avg1) # 此时avg1=15
    

    3 预激生成器的装饰器 案例

    def init(func):   #装饰器
        def inner(*args,**kwargs):
            g = func(*args,**kwargs)    #g = average()
            g.__next__() #将第一次调用的next方法,放到了装饰器中
            return g
        return inner
    
    @init
    def average():
        sum = 0
        count = 0
        avg = 0
        while True:
            num = yield avg
            sum += num    # 10
            count += 1    # 1
            avg = sum/count
    
    avg_g = average()   #===> inner
    ret = avg_g.send(10)
    print(ret)
    ret = avg_g.send(20)
    print(ret)
    

    小案例

    yield from 等同于 for ...in...

    def generator():
        a = 'abcde'
        b = '12345'
        for i in a:
            yield i
        for i in b:
            yield i
            
    g = generator()
    for i in g:
        print(i)
    # 结果:
    a
    b
    c
    d
    e
    1
    2
    3
    4
    5
    
    def generator():
        a = 'abcde'
        b = '12345'
        yield from a
        yield from b
    
    g = generator()
    for i in g:
        print(i)
    # 结果:
    a
    b
    c
    d
    e
    1
    2
    3
    4
    5
    
  • 相关阅读:
    步步为营 .NET 设计模式学习笔记 四、Singleton(单例模式)
    步步为营 .NET 设计模式学习笔记 九、Command(命令模式)
    步步为营 .NET 设计模式学习笔记 十八、Template(模板模式)
    步步为营 .NET 代码重构学习笔记 七
    步步为营 .NET 设计模式学习笔记 十九、Chain of Responsibility(职责链模式)
    步步为营 .NET 设计模式学习笔记 二十二、Memento(备望录模式)
    步步为营 .NET 设计模式学习笔记 十四、Decorator(装饰模式)
    步步为营 .NET 代码重构学习 十一
    步步为营 .NET 设计模式学习笔记 一、开篇(设计模式之泡妞二十三招)
    步步为营 .NET 设计模式学习笔记 七、Proxy(代理模式)
  • 原文地址:https://www.cnblogs.com/chenych/p/10939676.html
Copyright © 2011-2022 走看看