• 生成器


    一.生成器与yield

    若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象

    >>> def my_range(start,stop,step=1):
    ...     print('start...')
    ...     while start < stop:
    ...         yield start
    ...         start+=step
    ...     print('end...')
    ... 
    >>> g=my_range(0,3)
    >>> g
    <generator object my_range at 0x104105678>
    

    生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器

    >>> g.__iter__
    <method-wrapper '__iter__' of generator object at 0x1037d2af0>
    >>> g.__next__
    <method-wrapper '__next__' of generator object at 0x1037d2af0>
    

    因而我们可以用next(生成器)触发生成器所对应函数的执行,

    >>> next(g) # 触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
    start...
    0
    >>> next(g) # 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
    1
    >>> next(g) # 周而复始...
    2
    >>> next(g) # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代
    end...
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:

    >>> for i in countdown(3):
    ...     print(i)
    ... 
    countdown start
    3
    2
    1
    Done!
    

    有了yield关键字,我们就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值

    二.yield表达式应用

    在函数内可以采用表达式形式的yield

    >>> def eater():
    ...     print('Ready to eat')
    ...     while True:
    ...         food=yield
    ...         print('get the food: %s, and start to eat' %food)
    ...
    

    可以拿到函数的生成器对象持续为函数体send值,如下

    >>> g=eater() # 得到生成器对象
    >>> g
    <generator object eater at 0x101b6e2b0>
    >>> next(e) # 需要事先”初始化”一次,让函数挂起在food=yield,等待调用g.send()方法为其传值
    Ready to eat
    >>> g.send('包子')
    get the food: 包子, and start to eat
    >>> g.send('鸡腿')
    get the food: 鸡腿, and start to eat
    

    针对表达式形式的yield,生成器对象必须事先被初始化一次,让函数挂起在food=yield的位置,等待调用g.send()方法为函数体传值,g.send(None)等同于next(g)。

    ​ 我们可以编写装饰器来完成为所有表达式形式yield对应生成器的初始化操作,如下

    def init(func):
        def wrapper(*args,**kwargs):
            g=func(*args,**kwargs)
            next(g)
            return g
        return wrapper
    
    @init
    def eater():
        print('Ready to eat')
        while True:
            food=yield
            print('get the food: %s, and start to eat' %food)
    

    表达式形式的yield也可以用于返回多次值,即变量名=yield 值的形式,如下

    >>> def eater():
    ...     print('Ready to eat')
    ...     food_list=[]
    ...     while True:
    ...         food=yield food_list
    ...         food_list.append(food)
    ... 
    >>> e=eater()
    >>> next(e)
    Ready to eat
    []
    >>> e.send('蒸羊羔')
    ['蒸羊羔']
    >>> e.send('蒸熊掌')
    ['蒸羊羔', '蒸熊掌']
    >>> e.send('蒸鹿尾儿')
    ['蒸羊羔', '蒸熊掌', '蒸鹿尾儿']
    

    三.三元表达式、列表生成式、生成器表达式

    3.1 三元表达式
    三元表达式是python为我们提供的一种简化代码的解决方案,语法如下

    res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值
    
    针对下述场景
    
    def max2(x,y):
        if x > y:
            return x
        else:
            return y
    
    res = max2(1,2)
    
    用三元表达式可以一行解决
    
    x=1
    y=2
    res = x if x > y else y # 三元表达式
    

    3.2 列表生成式
    列表生成式是python为我们提供的一种简化代码的解决方案,用来快速生成列表,语法如下

    [expression for item1 in iterable1 if condition1
    for item2 in iterable2 if condition2
    ...
    for itemN in iterableN if conditionN
    ]
    
    #类似于
    res=[]
    for item1 in iterable1:
        if condition1:
            for item2 in iterable2:
                if condition2
                    ...
                    for itemN in iterableN:
                        if conditionN:
                            res.append(expression)
    
    针对下述场景
    
    egg_list=[]
    for i in range(10):
        egg_list.append('鸡蛋%s' %i)
        
    用列表生成式可以一行解决
    
    egg_list=['鸡蛋%s' %i for i in range(10)]
    

    3.3 生成器表达式
    创建一个生成器对象有两种方式,一种是调用带yield关键字的函数,另一种就是生成器表达式,与列表生成式的语法格式相同,只需要将[]换成(),即:

    (expression for item in iterable if condition)
    

    对比列表生成式返回的是一个列表,生成器表达式返回的是一个生成器对象

    >>> [x*x for x in range(3)]
    [0, 1, 4]
    >>> g=(x*x for x in range(3))
    >>> g
    <generator object <genexpr> at 0x101be0ba0>
    

    对比列表生成式,生成器表达式的优点自然是节省内存(一次只产生一个值在内存中)

    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
    >>> next(g) #抛出异常StopIteration
    

    如果我们要读取一个大文件的字节数,应该基于生成器表达式的方式完成

    with open('db.txt','rb') as f:
        nums=(len(line) for line in f)
        total_size=sum(nums) # 依次执行next(nums),然后累加到一起得到结果=
    
  • 相关阅读:
    poj 2528 Mayor's posters (线段树+离散化)
    poj 1201 Intervals (差分约束)
    hdu 4109 Instrction Arrangement (差分约束)
    poj 1195 Mobile phones (二维 树状数组)
    poj 2983 Is the Information Reliable? (差分约束)
    树状数组 讲解
    poj 2828 Buy Tickets (线段树)
    hdu 1166 敌兵布阵 (树状数组)
    Ubuntu网络配置
    Button控制窗体变量(开关控制灯的状态)
  • 原文地址:https://www.cnblogs.com/chenwenyin/p/12557680.html
Copyright © 2020-2023  润新知