• Python的生成器


    一 生成器与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可以保存函数的运行状态挂起函数,用来返回多次值。

      总结:return和yield的区别?
      (1)return:只能返回一次值,就结束函数。
      (2)yield:可以返回多次值。
      (3)yield:可以将函数暂停到某个位置。
      (4)return和yield不能混用。

    二 yield表达式应用

    # 如何得到自定义的迭代器?
    # (1)在函数内存在yield关键字.
    # (2)调用函数并不会执行函数体代码.
    # (3)会返回一个生成器对象,生成器即自定义的迭代器
    def func():
        # return  # 返回函数内的值,但是只能返回一个值,程序遇到return就会跳出.
        print("第一次")
        yield 1
        print("第二次")
        yield 2
        print("第三次")
        yield 3
        print("第四次")
    
    g = func()
    print(g)
    # 运行结果是: <generator object func at 0x0000025A10E4F660>  generator翻译:生成器,其实生成器就是迭代器,只不过是自定义出来的.
    
    # 如何使用这个自定义的生成器?
    # g.__iter__()
    # print(g.__iter__())
    
    # 会触发函数体代码的运行,然后遇到yield停下来.将yield后面的值当作本次调用的结果返回
    # g.__next__()
    print(g.__next__())  # 第一次 1
    print(g.__next__())  # 第二次 2
    print(g.__next__())  # 第三次 3
    print(g.__next__())  # 没有返回值之后报错 :StopIteration
    # 统计字符串长度
    print("aaa".__len__())
    len("aaa")  # 本质是"aaa".__len__()
    
    next(g)  # 本质是g.__next__()
    # iter(可迭代对象)  本质可迭代对象.__iter__()

     yield表达式的应用(一)

    # 简单的理解yield
    def func():
        print("start....")
        x = yield 111  #
        print("哈哈哈哈")
        yield 222
        # print("stop....")
    
    g = func()
    res = g.send(None)  # 必须先运行一下g.send(None)等同于next(g)
    print(res)  # 返回值是 111
    
    res = g.send("lsj")  #
    print(res)  # 返回值是 222
    """
    start....
    111
    哈哈哈哈
    2222
    """

     

    # yield后不跟返回值
    #
    x = yield返回值(默认返回值为None)一定在函数内的生成器 # 把生成器当作一条狗喂狗投食 def dog(name): print("道哥%s准备吃东西了..."%name) while True: # x = yield None # 不写返回值表示返回值是None # x拿到的是yield接受到的值 x = yield # 不写返回值表示返回值是None print("道哥%s吃了%s"%(name,x)) g = dog("alex") # print(g) # <generator object dog at 0x0000021DC24AF430> # print(next(g)) # 道哥lsj准备吃东西了 None # res = next(g) # print(res) # None # next(g) # 道哥alex吃了None,遇到yield停下来等待 # 使用send为x传值:也就是seed为yield赋值,yield在传给x # g.send("一根骨头") # TypeError: can't send non-None value to a just-started generator g.send(None) # 等同于next(g) # 测试g.send(None)等同于next(g) # g.send(None) # 道哥alex吃了None # next(g) # 道哥alex吃了None # 再次传值可以是字符串、可以是类型的数据 g.send("一根骨头") # 道哥alex吃了一根骨头 g.send("肉包子") # 道哥alex吃了肉包子 g.send([1,2,3]) # 道哥alex吃了[1, 2, 3] # 总结有yield和没有yield的区别? # (1)有yield的生成器,运行到yield可以暂停。等待传值后在运行。 # (2)没有yield的函数在运行时,不停止一直运行。 # 关闭g # g.close() # g.send("1111") # 关闭后再次传值会报错StopIteration

     yield表达式的应用(二)

    # yield表达式的应用(二)了解
    # x = yield 返回值(默认返回值为None)一定在函数内的生成器
    # 把生成器当作一条狗喂狗投食
    def dog(name):
        food_list = []  # 定义一个空列表
        print("道哥%s准备吃东西了..."%name)
        while True:
            # x = yield None # 不写返回值表示返回值是None
            # x拿到的是yield接受到的值(值先给yield后给x)
            # x = yield 1111 # yield后跟返回值:1111先赋值给yield后把yield赋值给x
            x = yield food_list # yield后跟空列表
            print("道哥%s吃了%s"%(name,x))
            food_list.append(x)
    
    g = dog("alex")
    # g.send(None)  # 道哥alex准备吃东西了...
    # res = g.send(None)  # 道哥alex准备吃东西了...
    # print(res)  # 1111
    
    # 加入send功能x
    # res = g.send("肉包子")
    # print(res)
    #
    # res = g.send(["a","b"])
    # print(res)
    
    # 向定义的列表内传参
    res = g.send(None)
    print(res)  # []
    
    res = g.send("一根骨头")
    print(res)  # ['一根骨头']
    
    res = g.send("肉包子")
    print(res)  # ['一根骨头', '肉包子']

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

    • 三元表达式
    # 需求: 比较两个参数的大小,返回较大值
    # def func(x,y):
    #     if x > y:
    #         return x
    #     else:
    #         return y
    #
    # res = func(1,2)
    # print(res)
    
    # 使用三元表达式,解决上面的需求
    # 语法格式:条件成立时返回的值 if 条件 else 条件不成立时返回的值
    x = 1
    y = 2
    res = x if x > y else y 
    print(res)
    • 列表生成式
    • 列表生成式是python为我们提供的一种简化代码的解决方案,用来快速生成列表,语法如下
    [expression for item1 in iterable1 if condition1
    for item2 in iterable2 if condition2
    ...
    for itemN in iterableN if conditionN
    ]
    l = ['lsj','lxx_dsb','alex_dsb','wxx','xxq_dsb']
    # 需求:将含有dsb放到新列表里
    # new_l = []
    # for name in l:
    #     if name.endswith('dsb'):
    #          new_l.append(name)
    # print(new_l)
    
    # 列表生成器的目的是将上述代码更精简
    # new_l = [name for name in l if name.endswith('dsb')]  # ['lxx_dsb', 'alex_dsb', 'xxq_dsb']
    # new_l = [111 for name in l if name.endswith('dsb ')]  # [11, 11, 11]
    new_l = [name for name in l ]  # ['lsj', 'lxx_dsb', 'alex_dsb', 'wxx', 'xxq_dsb']
    print(new_l)
    # 案例
    # 把所有小写字母全变成大写
    new_l = [name.upper() for name in l ]
    print(new_l)
    # 把所有的名字去掉后缀_dsb
    # new_l = [name.split('_')[0] for name in l ]  # 方式一
    new_l = [name.replace('_dsb','') for name in l ]  # 方式二
    print(new_l)
    • 生成器表达式
    l = [表达式 for x in 可迭代对象 if 条件]
    g = (表达式 for x in 可迭代对象 if 条件)  next(g)
    sum(表达式 for x in 可迭代对象 if 条件)
    list(表达式 for x in 可迭代对象 if 条件)
    dic = {键:值 for k in 可迭代对象 if 条件}
    set = {元素 k in 可迭代对象 if 条件}
    # (1)字典生成式
    keys = {'name','age','gender'}
    dic = {key:None for key in keys}
    print(dic,type(dic))  # {'age': None, 'gender': None, 'name': None} <class 'dict'>
    
    # 把下面的列表转换成字典,并且不要gender的数据
    items =[('name','lsj'),('age',18),('gender','male')]
    dic = {k:v for k,v in items if k != 'gender'}
    print(dic,type(dic))  # {'name': 'lsj', 'age': 18} <class 'dict'>
    
    # (2)集合生成式
    set = ['name','age','gender']
    set1 = {k for k in set}
    print(set1,type(set1))  # {'age', 'name', 'gender'} <class 'set'>
    
    # (3)元组生成式,从0到10一组数
    g = (i for i in range(10) if i > 3)
    print(g,type(g))  # 得到不是元组(因为元组式不可变数据类型),所以没有元组生成式,这是一个生成器表达式.
                    #  <generator object <genexpr> at 0x0000022B79C4F7B0> <class 'generator'>
    
    # (4)针对生成器的玩法?
    ge = (i for i in range(10) if i > 3)
    # 强调:此刻ge内部一个值也没有,没等到next(ge)的时候取出一个值,再next(ge)的时候再取出一个值
    print(ge)
    print(next(ge))  # 4
    print(next(ge))  # 5
    # 应用:统计笔记.txt文件里的字符数 sum()
    
    # print(sum([1,2,3,4,5]))  # sum()对传入的可迭代对象进行累加,运行结果是:15
    # 方式一:
    # with open('笔记.txt',mode='rt',encoding='utf-8') as f:
    #     res = 0
    #     for line in f:
    #         res += len(line)
    #     print(res)
    
    # 方式二:如果文件行过多,此时文件内容放入f的数据太多
    # with open('笔记.txt',mode='rt',encoding='utf-8') as f:
    #     size_of_line = [len(line) for line in f]
    #     print(size_of_line)  # 每一行的长度 [11, 1]
    #     res = sum(size_of_line)
    #     print(res)
    
    # 方式三:效率最高,不受方式二文件行数过多影响
    # with open('笔记.txt',mode='rt',encoding='utf-8') as f:
    #     g = (len(line) for line in f)
    #     print(g)
    #     res = sum(g)
    #     print(res)
    # 方式三:优化
    with open('笔记.txt',mode='rt',encoding='utf-8') as f:
        # res = sum((len(line) for line in f)) # 运行结果: 10 
        res = sum(len(line) for line in f)  # 同上的效果  10
        print(res)

      

  • 相关阅读:
    调研《构建之法》指导下的历届作品
    需求分析与原型设计
    软件工程的实践项目课程的自我目标
    php 字符串的一些操作,以便记忆
    MOTT的学习(一)
    session,cookie
    php Curl_setop 的学习
    使用Git进行代码管理的心得
    搭建andiord sdk和安装eclipse adt插件的个人小体会
    这些天自身努力的体会,关于java方面的
  • 原文地址:https://www.cnblogs.com/liunaixu/p/12657021.html
Copyright © 2020-2023  润新知