zoukankan      html  css  js  c++  java
  • 26、Python之生成器

    一、生成器与yield

    1.1 如何得到自定义的迭代器

    # 在函数内一旦存在yield关键字,调用函数并不会执行函数体代码,会返回一个生成器对象
    def func():
        print('第一次')
        yield 1
        print('第二次')
        yield 2
        print('第三次')
        yield 3
        print('第四次')
        
    g=func()
    # print(g)  # <generator object func at 0x0000016B514AE9E0>
    # 生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器
    # g.__iter__()
    # g.__next__()
    
    # 因而我们可以用next(生成器)触发生成器所对应函数的执行,然后遇到yield停下来,将yield后的值当做本次调用的结果返回
    res1=g.__next__()
    print(res1)
    
    res2=g.__next__()
    print(res2)
    
    res3=g.__next__()
    print(res3)
    
    res4=g.__next__()  # 第四次
    					# StopIteration
    
    len('aaa') # 'aaa'.__len__()
    
    next(g)    # g.__next__()
    
    iter(可迭代对象)     # 可迭代对象.__iter__()
    

    1.2 小案例分析

    # # 应用案列
    def my_range(start,stop,step=1):
        # print('start...')
        while start < stop:
            yield start    #  函数中出现了yield,调用时已不能执行函数体代码,实际上已变成一个生成器generator
            start+=step
        # print('end....')
    
    
    g=my_range(0,3) # <generator object my_range at 0x000001891CC0D9E0>
    print(next(g))  # start...    0  若不注释print,触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
    print(next(g))  # 1  再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
    print(next(g))  # 2  重复上一步
    print(next(g))  #   触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代StopIteration	
    				# end...
        
    ## 既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:
    for n in my_range(1,3):
        print(n) # 0、1、2
    
    # 总结yield:
    # 有了yield关键字,我们就有了一种自定义迭代器的实现方式。
    # yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值。
    

    二、yield表达式应用

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

    def dog(name):
        print('道哥%s准备吃东西啦...' %name)
        while True:
            # x拿到的是yield接收到的值
            x = yield # x = '肉包子'
            print('道哥%s吃了 %s' %(name,x))
    
    g=dog('qwert') # 得到生成器对象
    # print(g)  # <generator object dog at 0x00000296629F7190>
    g.send(None) # 等同于next(g),若不传空值进行'初始化',则会报错:TypeError: can't send non-None value to a just-started generator
    
    g.send(['一根骨头','aaa'])  # 道哥qwert准备吃东西啦...  道哥qwert吃了 ['一根骨头', 'aaa']
    g.send('肉包子')  # 道哥qwert吃了 肉包子
    g.send('一桶泔水')  # 道哥qwert吃了 一桶泔水
    g.close()
    g.send('1111') # StopIteration 关闭之后无法传值
    

    针对表达式形式的yield,生成器对象必须事先被初始化一次,让函数挂起在表达式x=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 dog(name):
        print('道哥%s准备吃东西啦...' %name)
        while True:
            x = yield 
            print('道哥%s吃了 %s' %(name,x))
    

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

    def dog(name):
        food_list=[]
        print('道哥%s准备吃东西啦...' %name)
        while True:
            # x拿到的是yield接收到的值
            x = yield food_list # x = '肉包子'
            print('道哥%s吃了 %s' %(name,x))
            food_list.append(x) # ['一根骨头','肉包子']
    
    g=dog('qwert')
    res=g.send(None)  # next(g)
    print(res)  # 道哥qwert准备吃东西啦...      []
    
    res=g.send('一根骨头')
    print(res)  # 道哥qwert吃了 一根骨头        ['一根骨头']
    
    res=g.send('肉包子')
    print(res)  # 道哥qwert吃了 肉包子     ['一根骨头', '肉包子']
    
    
    def func():
        print('start.....')
        x=yield 1111  # x='xxxxx'
        print('哈哈哈啊哈')
        print('哈哈哈啊哈')
        print('哈哈哈啊哈')
        print('哈哈哈啊哈')
        yield 22222
    
    g=func()
    res=next(g)
    print(res)  # start..... / 1111
    
    res=g.send('xxxxx')
    print(res)  # 哈哈哈啊哈 / 哈哈哈啊哈 / 哈哈哈啊哈 / 哈哈哈啊哈 / 22222
    

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

    3.1 三元表达式

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

    res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值
    

    针对下述场景

    def func(x,y):
        if x > y:
            return x
        else:
            return y
    
    res=func(1,2)
    print(res)  # 2
    

    用三元表达式可以一行解决

    x=1
    y=2
    
    res=x if x > y else y
    print(res)  # 2
    

    3.2 列表生成式

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

    # 1、列表生成式:
    l = ['alex_nb', 'lxx_nb', 'wxx_nb', "xxq_nb", 'egon']
    # 取出所有'nb'结尾的组成新的列表
    new_l=[]
    for name in l:
        if name.endswith('nb'):
            new_l.append(name)
    print(new_l)  # ['alex_nb', 'lxx_nb', 'wxx_nb', 'xxq_nb']
    
    new_l=[name for name in l]  # 快速生成新的列表,['alex_nb', 'lxx_nb', 'wxx_nb', 'xxq_nb', 'egon']
    
    new_l=[name for name in l if name.endswith('nb')]  # ['alex_nb', 'lxx_nb', 'wxx_nb', 'xxq_nb']
    
    # 把所有小写字母全变成大写
    new_l=[name.upper() for name in l]
    print(new_l)  # ['ALEX_NB', 'LXX_NB', 'WXX_NB', 'XXQ_NB', 'EGON']
    
    # 把所有的名字去掉后缀_nb
    new_l=[name.replace('_nb','') for name in l]
    print(new_l)  # ['alex', 'lxx', 'wxx', 'xxq', 'egon']
    
    # 2、字典生成式
    keys=['name','age','gender']
    dic={key:None for key in keys}
    print(dic)  # {'name': None, 'age': None, 'gender': None}
    
    items=[('name','egon'),('age',18),('gender','male')]
    res={k:v for k,v in items if k != 'gender'}
    print(res)  # {'name': 'egon', 'age': 18}
    
    # 3、集合生成式
    keys=['name','age','gender']
    set1={key for key in keys}
    print(set1,type(set1))  # {'gender', 'age', 'name'} <class 'set'>
    
    

    3.3 生成器表达式

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

    (expression for item in iterable if condition)
    

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

    g=(i for i in range(6) if i > 3)
    # !!!!!!!!!!!强调!!!!!!!!!!!!!!!
    # 此刻g内部一个值也没有
    
    print(g,type(g))  # <generator object <genexpr> at 0x000001F1CE13D9E0> <class 'generator'>
    
    print(next(g))  # 4
    print(next(g))  # 5
    print(next(g))  # StopIteration
    

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

    with open('笔记.txt', mode='rt', encoding='utf-8') as f:
        # 方式一:
        res=0
        for line in f:
            res+=len(line)
        print(res)
    
        # 方式二:
        res=sum([len(line) for line in f])
        print(res)
    
        # 方式三 :效率最高
        res = sum((len(line) for line in f))
        # 上述可以简写为如下形式
        res = sum(len(line) for line in f)
        print(res)
    

    四、叠加多个装饰器(了解)

    叠加多个装饰器的加载、运行分析:

    def deco1(func1): # func1 = wrapper2的内存地址
        def wrapper1(*args,**kwargs):
            print('正在运行===>deco1.wrapper1')
            res1=func1(*args,**kwargs)
            return res1
        return wrapper1
    
    def deco2(func2): # func2 = wrapper3的内存地址
        def wrapper2(*args,**kwargs):
            print('正在运行===>deco2.wrapper2')
            res2=func2(*args,**kwargs)
            return res2
        return wrapper2
    
    def deco3(x):
        def outter3(func3): # func3=被装饰对象index函数的内存地址
            def wrapper3(*args,**kwargs):
                print('正在运行===>deco3.outter3.wrapper3')
                res3=func3(*args,**kwargs)
                return res3
            return wrapper3
        return outter3
    
    # 加载顺序自下而上(了解)
    @deco1      # index=deco1(wrapper2的内存地址)        ===> index=wrapper1的内存地址
    @deco2      # index=deco2(wrapper3的内存地址)        ===> index=wrapper2的内存地址
    @deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址
    def index(x,y):
        print('from index %s:%s' %(x,y))
    
    # 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3
    index(1,2) # wrapper1(1,2)
    
    
  • 相关阅读:
    我的2015---找寻真实的自己
    JQuery日记 5.31 JQuery对象的生成
    UVA
    elk 日志分析系统Logstash+ElasticSearch+Kibana4
    OAF 中下载使用XML Publisher下载PDF附件
    page上BeanId与ActionType中的ParameterId
    OAF 功能中的参数含义
    EBS管理员为供应商创建新联系人流程
    EBS Workflow参考资料
    采购模块设置快码
  • 原文地址:https://www.cnblogs.com/zuiyouyingde/p/12559504.html
Copyright © 2011-2022 走看看