zoukankan      html  css  js  c++  java
  • 初识生成器与生成器表达式 Day12

    一、生成器

    1,生成器基本概念

    生成器的实质是迭代器

    迭代器:Iterator  内部同时包含了__iter__()和__next__()函数
    可迭代对象:Iterable  内部包含__iter__()函数
    迭代器的特点:(同时也是生成器特点)
      1,节省内存
      2,惰性机制
      3,不能反复只能向下执行

    在Python中有三种方法来获取生成器:

    1. 通过生成器函数

    2. 通过各种推导式来实现生成器

    3.通过数据的转换也可以获取生成器

    示例:先看一个简单的函数:

    def func():
        print("111")
        return 222
    
    ret = func()
    print(ret)
    
    结果:
    111
    222

    再将函数中的return换成yield变成生成器函数:

    def func():
        print("111")
        yield 222
    
    ret = func()
    print(ret)
    
    结果:
    <generator object func at 0x10647a8e0>

    可以看出结果是不一样的。由于函数中存在了yield,那么这个函数就是一个生成器函数。我们再执行这个函数的时候,就不再是函数的执行了,而是获取这个生成器。

    因为生成器的本质是迭代器,所以我们可以直接执行__next__()以下生成器:

    def func():
        print("111")
        yield 222
    
    gener = func()  # 这个时候函数不会执行,只是获取到生成器
    ret = gener.__next__()  # 函数执行,yield和return的作用一样,返回数据
    print(ret)
    
    结果:
    111
    222

    所以,yield和return的效果是一样的,都是返回数据,但还是有很大区别的:

    yield是分段来执行一个函数;return是直接停止执行函数

    2,生成器的send()方法:

    send()和__next__()一样都可以让生成器执行到下一个yield

    def eat():
        print("meat")
        a = yield "milk"
        print('baozi', a)
        b = yield 'zhou'
        print('guobaorou', b)
        yield 'tea'
        
    gen = eat()
    ret = gen.__next__()
    print(ret)
    
    ret = gen.__next__()
    print(ret)
    
    ret = gen.send("MIANBAO")  # 给上一个yield传值
    print(ret)
    
    ret = gen.send("DANGAO")  # 给上一个yield传值
    print(ret)
    
    
    结果:
    meat
    milk
    baozi None
    zhou
    guobaorou MIANBAO
    tea
    
    Traceback (most recent call last):
        ret = gen.send("DANGAO")  # 给上一个yield传值
    StopIteration

    send()和__next__():

      1. send和next都是让生成器向下走一次

      2. send可以给上一个yield的位置传递值,不能给最后一个yield发送值。也不能在第一次执行生成器代码的时候不能使用send()

    3,使用for循环

    def func():
        yield "a"
        yield "b"
        yield "c"
    
    gen = func()
    
    for el in gen:
        print(el)
    
    # 结果:
    # a
    # b
    # c

    使用for循环的话会获取内部所有的元素

    二、列表推导式,生成器表达式以及其他推导式

    1,列表推导式

    常用写法:[结果 for循环 if筛选]

    筛选模式:[结果 for 变量 in 可迭代对象 if 条件]

    练习题:

    # (1)创建列表:[1,3,5,7,9,...99]
    l2 = [i for i in range(1,100) if i % 2 == 1]  # [结果 for循环 if筛选]
    print(l2)
    # [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]
    
    # 列表推导式
    
    # (1) 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
    lst = ['hello', 'world', 'hh', 'old', 'boy', 'H']
    l1 = [i.upper() for i in lst if len(i) >= 3]
    print(1, l1)
    
    
    # (2) 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
    
    l2 = [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1]
    # l2 = list(zip([x for x in range(6) if x % 2 == 0], [y for y in range(6) if y % 2 == 1]))
    print(2, l2)
    
    
    # (3) 求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]]
    
    M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    l3 = [el[2] for el in M]
    print(3, l3)
    
    
    # (4) 求出50以内能被3整除的数的平方,并放入到一个列表中。
    
    l4 = [i*i for i in range(1, 50) if i % 3 == 0]
    print(4, l4)
    l4_1 = [i for i in range(1, 50) if i % 3 == 0 and i*i < 50]
    print(l4_1)
    
    
    # (6) 构建一个列表:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
    # l6 = [(x, y) for x in range(6) for y in range(1, 7)]
    l6 = [(x, x+1) for x in range(6)]
    print(6, l6)
    
    
    # (7) 构建一个列表:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    l7 = [i for i in range(0, 19, 2)]
    print(7, l7)
    
    
    # (8)有一个列表l1 = ['alex', 'WuSir', '老男孩', '太白']将其构造成这种列表['alex0', 'WuSir1', '老男孩2', '太白3']
    
    ll = ['alex', 'WuSir', '老男孩', '太白']
    l8 = [v+str(i) for i, v in enumerate(ll)]
    print(8, l8)
    # l8 = []
    # for i,v in enumerate(ll):
    #     v = v+str(i)
    #     print(v,i)
    
    
    # (9)有以下数据类型:
    x = {
        'name': 'alex',
        'Values': [
            {'timestamp':1517991992.94,
             'values':100,},
            {'timestamp': 1517992000.94,
            'values': 200,},
            {'timestamp': 1517992014.94,
             'values': 300,},
            {'timestamp': 1517992744.94,
             'values': 350},
            {'timestamp': 1517992800.94,
             'values': 280}
    ], }
    # 将上面的数据通过列表推导式转换成下面的类型:
    # [[1517991992.94, 100], [1517992000.94, 200], [1517992014.94, 300], [1517992744.94, 350], [1517992800.94, 280]]
    
    l9 = [[el['timestamp'], el['values']] for el in x['Values']]
    print(9, l9)
    
    # 结果:
    1 ['HELLO', 'WORLD', 'OLD', 'BOY']
    2 [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
    3 [3, 6, 9]
    4 [9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304]
    [3, 6]
    6 [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
    7 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    8 ['alex0', 'WuSir1', '老男孩2', '太白3']
    9 [[1517991992.94, 100], [1517992000.94, 200], [1517992014.94, 300], [1517992744.94, 350], [1517992800.94, 280]]
    # (5) 寻找名字中带有两个e的人的名字
    names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven','Joe'],
     ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
    # l5 = []
    # for i in range(len(names)):
    #     for j in names[i]:
    #         if j.count('e') >= 2:
    #             l5.append(j)
    l5 = [j for i in range(len(names)) for j in names[i] if j.count('e') >= 2]
    print(l5)
    
    # 主要注意str.count()的运用,查找字符串数量

    2,生成器表达式:

    语法与列表推导式基本上是一样的。只是把 [] 替换成 {}

    gen = (i for i in range(10))  # 生成表达式
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    # print(gen.__next__())  # 超出数量会报错
    
    # 结果:
    # 0
    # 1
    # 2
    # 3
    # 4
    # 5
    # 6
    # 7
    # 8
    # 9

    生成器表达式与列表推导式的区别:
      1,列表推导式比较耗内存,一次性加载;而生成器表达式几乎不占用内存,使用的时候才分配和使用内存。

      2,得到的值不一样:列表推导式得到的是一个列表,生成器表达式获得的是一个生成器。

    生成器的惰性求值:生成器只有在访问的时候才取值。

      

  • 相关阅读:
    Python那些优雅的写法:switch-case
    python将print输出的信息保留到日志文件中
    Python 获取被调用函数名称,所处模块,被调用代码行
    python **运算符及多参数传参
    使用Docker之镜像的拉取、查询、删除
    资料
    kmp算法
    开源软件
    golang之http请求的dns解析代码流程
    Go语言从HTTP请求中获取服务端IP地址
  • 原文地址:https://www.cnblogs.com/russellyoung/p/9671249.html
Copyright © 2011-2022 走看看