• 生成器、列表推导式、生成器表达式


    一、生成器

    生成器,就是自己用代码写的迭代器,生成器的本质就迭代器。

    之前的,将一个可迭代对象 转化 成为迭代器。

    li = [1,2,3,5,18]    # 这是一个列表 可迭代对象

    iter1 = li.__iter__()   # 将一个迭代对象 转换成 迭代器 

    iter1 = iter(li)    # 这个是内置函数,本质上还是调用对象的__iter__()方   

           x = 15
    iter(x) # TypeError: 'int' object is not iterable
    上面的li 是python源码提供的基本数据类型之一 列表,而 __iter__() 或者 iter()都是源码提供的,不是我们自己写的

    用以下两种方式构建一个生成器:

      1,通过生成器函数

      2,生成器表达式  

    生成器函数:

    # 生成器函数
    # 首先来看一看,普通函数的定义
    def func1(x):
        x += 1
        print(x)
        return x
    result = func1(10)   # 对于普通函数,函数名()就是调用或执行函数,将会执行函数体的代码
    print(result)
    # 生成器函数
    def generator_func1(x):
        x += 1
        print(333)
        yield x
        x += 3
        print(444)
        yield x
    g_obj = generator_func1(10)  # 调用或执行生成器函数,并不会执行函数体,而是在内存中创建一个生成器函数对象
    # print(g_obj)  # <generator object generator_func1 at 0x00000145FDCD0E60>
    
    # 问题来了?那么如何 执行 生成器函数对象 对应的 函数的函数体呢? 答案,调用生成器函数对象的__netx__()方法,即生成器的本质就是迭代器
    v1 = g_obj.__next__()  # 11 当第一次调用生成器对象的__next__()方法时,从对应的生成器函数开头,从上往下 向下执行,当遇到第一个yield,返回yield后面的
                          # 值 ,函数挂起。
    print(v1)   # 11
    
    v2 = g_obj.__next__()  # 14 # 从函数上次执行到的yield语句 下 的第一条语句,继续向下执行,下到遇到yield语句,返回yield后面的值
    print(v2)
    
    v3 = g_obj.__next__()  #   v3 = g_obj.__next__() StopIteration   当取值都取完了,再取就报错,停止迭代
    
    # 总结 : 一个__next__() 或者 next(g_obj) 方法  对应一个 yield 
    # yield 将其后面的值,如果是一个数据,原封不动返回,如果多个值 ,放在元组里返回,将值返回给  生成器对象.__next__()方法
    
    # 生成器函数 yield  与 普通函数  return 的区别
    # return 结束函数,给函数的执行者返回值
    # yield 不会结束函数,一个next 对应 一个 yield , 给生成器对象.__next__() 返回值 。

    生成器函数 vs 迭代器:

    # 生成器函数  VS 迭代器
    # 区别1: 自定制的区别
    l1 = [1,2,3,4,5]  # 比较死板,不灵活
    iter1 = iter(l1)
    
    def func1(x):
        x += 1
        yield x
        x += 3
        yield x
        x += 5
        yield x
    g1 = func1(5)   # 国为生成器,是通过生成器函数 创建的, 函数可以传参,就显得比较灵活
    print(g1.__next__())
    print(g1.__next__())
    print(g1.__next__())
    
    # 区别2 : 内存级别的区别
    # 迭代器需要通过可迭代对象进行转化。 可迭代对象非常占用内存
    # 生成器直接创建,不需要转化,从本质上就节省内存。
    
    def func1():
        for i in range(1,100000):
            yield i
    g2 = func1()
    for i in range(50):
        print(g2.__next__())
    
    count = 1
    while count < 51:
        print(g2.__next__())
        count += 1
    
    
    # send 与  __next__()区别:
    
    def func10():
        print(111)
        count = yield 6
        print(222)
        print(count)
        count1 = yield 7
        print(count1)
        yield 8
    g10 = func10()
    print(g10.__next__())  # 111 6
    print(g10.send("send1"))   # 222 send1 7
    print(g10.send("send2"))   # send2 8
    
    # send 与 next()方法,或者 生成器对象.__next__()方法一样,也是对生成器取值的方法。取值就是执行一个yield
    # send 可以上一个yield 整个表达式传值 ,如果yield 左边有变量接受,则赋值给它。
    # 第一次取值 永远都是next
    # 最后一个yield 永远也得不到send传的值。
    
    def  cloth(n):
        for i in range(1,n+1):
            yield "第%d件衣服成功产出" % i
    g_cloth = cloth(1000)   # 创建了一个可以产生1000件衣服的生成器,但是是一件一件地产出的
    
    # print(g_cloth.__next__())
    # print(g_cloth.__next__())
    
    # 下指令,生产50件衣服
    # for i in range(50):
    #     print(g_cloth.__next__())
    def  cloth2(n):
        for i in range(1,n+1):
            yield "第%d件衣服成功产出" % i,"共消耗成本{}元".format(i * 85)
    g_cloth2 = cloth2(10000)
    count = 1
    while count < 51:
        print("".join(list(next(g_cloth2))))
        # print(next(g_cloth2))
        count += 1
    def cloth2(n):
    for i in range(n+1):
    yield "衣服 %s" % i
    generator2 = cloth2(1000)
    for num in range(51): # range(5) ,也是一个迭代对象,数字范围太大,也占用内存间
    print(generator2.__next__()) # 调用生成器函数对象的__next__() 或者 send() 方法,将推动 对应的生成器函数执行,
    # 执行到,遇到下一个yield 语句,函数挂起,下次调用__next__() 或者 send() 方法,上一个yield
    # 语句之后,继续 执行,直到下一个yield语句,依次类推。
    count = 1
    while count < 51: # 继续生产50件
    print(next(generator2))
    count += 1


    def fuc_gene():
    x = 5
    print(x)
    yield x,10
    x = {'name':"chris",'age':18}
    print(x)
    yield x,[1,2,3] # 返回时,多个值放到一个元组中,和return一样
    gene = fuc_gene() # 直接调用或执行生成器函数,并不会执行其函数体,而是在内存中创建一个生成器函数对象
    print(gene.__next__()) # 通过调用生成器函数对象的__next__() 或者 send()方法来,来推动与之对应的生成器函数的陈函数体的执行。
    print(gene.__next__())

    # 列表推导模式:一行代码几乎搞定你需要的任何的列表
    # 循环模式 [变量(加工后的变量) for 变量 in iterable ]
    l = [i for i in range(1,101)]
    print(l)

    l2 = ["开学第%d几天" % i for i in range(1,101)]
    print(l2)
    l3 = [i*i for i in range(1,11)]
    print(l3)

    # 筛选模式 [ 变量 (加工后的变量) for 变量 in iterable if 条件 ]
    l4 = [i for i in range(1,31) if i % 2 == 0]
    print(l4)
    l5 = [i**2 for i in range(1,31) if i % 3 == 0]
    print(l5)
    names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
    ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

    name = [name for list1 in names for name in list1 if name.count("e") == 2]
    print(names)
    print([j for i in names for j in i if j.count("e") == 2])
    print(name)

    # 列表推导式
    # 优点:一行解决,方便。
    # 缺点:容易着迷,不是易排错,不能过超过三次循环
    # 列表推导式不能解决所有列表的问题,所以不要太刻意用。

    # 生成器表达式:将列表推导式的 [] 转换成 () 即可
    g = (i for i in range(1,11)) # 得到一个生成器
    print(g) # <generator object <genexpr> at 0x000001D0C61922B0>
    # print(g.__next__())
    # while True:
    # try:
    # print(next(g))
    # except StopIteration:
    # break
    # li_test = [print(5) for i in range(5)] # 打印 5 次 5
    # print(li_test) # [None, None, None, None, None]

    #
    test2 = [print(10) for i in range(5)] # 遍历 for 循环中的 可迭代对象,每取一个值 ,执行for循环前面的语句,将值 追加到列表当中,
    # print()没有返回值 ,所以是None, ...注意:列表推导式,for 前面只能一条语句,多条的话,就
    # 要考虑用函数来封装,且执行列表推导式时,函数执行
    def tell(n):
    print("追加第%d个元素到当前列表中" % n)
    return n

    test3 = [tell(i) for i in range(1,6)]
    '''
    输出结果:
    追加第1个元素到当前列表中
    追加第2个元素到当前列表中
    追加第3个元素到当前列表中
    追加第4个元素到当前列表中
    追加第5个元素到当前列表中
    '''
    print(test3) # [1, 2, 3, 4, 5]

    # 生成器表达式扩展

    sum2 = 0
    def sum1(n): # 刚才犯一个错误,将全局变量和函数名,取名一样,我们说函数名就是特殊的变量,不能重名,否则执行时会出现意想不到的错误
    global sum2
    sum2 += n
    return sum2
    # print(sum2)


    g1 = (sum1(i) for i in range(1,11)) # 列表生成器表式式,会将普通函数的 return 语句 转换成 yield 语句
    print(g1)
    print(sum2)
    print(g1.__next__())
    print(g1.__next__())
    print(g1.__next__())

    print("-*"*30)
    # 我的猜想,然后,再根据 for 循环的次数变成生成器函数:
    sum2 = 0
    def g1_func(): #
    start = 1
    while start < 11: # 这样,就节省内存空间,不需要事先创建可迭代对象
    global sum2
    sum2 += start
    start += 1
    yield sum2
    g2 = g1_func()
    print(g2.__next__())
    print(g2.__next__())
    print(g2.__next__())


    # 字典推导式:
    mcase = {'a':10,'b':34}
    mcase_frequency = {mcase[k]: k for k in mcase}
    print(mcase_frequency)
    mcase.get('a')
    # 其它推导式:
    # 合并大小写对应的value值,将k统一成小写
    mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
    # mcase_frequency = {k.lower(): mcase.get(k.lower(),0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
    # print(mcase_frequency)

    # dict.get(key, default=None)
    print(mcase.get('b',1))
    print(reversed([1,3,5]))


    # 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
    li2 = [(x,y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1]
    print(li2)
    li2_iter = ((x,y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1)
    print(li2_iter) #<generator object <genexpr> at 0x000001F4D2FDC200>


    for i in li2_iter: # 迭代器,也可以for 循环
    print(i)
    # def g_function():
    #     print(555)
    #     ret1 = yield {1,3}
    #     print(666)
    # g = g_function()
    # print(g.__next__())
    # print(g.__next__())    # 一个 next 对应一个 yield , 生成器函数 ,只有一个yield 所以报错
    
    
    def g_function():
        print(555)
        ret1 = yield {1,3}
        print(ret1)
        print(666)
        yield "success"
    g = g_function()
    print(g.__next__())
    # print(g.__next__())  # None 666 success
    print(g.send("hello world"))   # 上一个yield 表达式,传值 ,赋值 给 左边的变量    # hello world 666 success

    生成器函数 与 普通函数的区别:

    def fuc_gene():
        x = 5
        print(x)
        yield x,10
        x = {'name':"chris",'age':18}
        print(x)
        yield x,[1,2,3]   # 返回时,多个值放到一个元组中,和return一样
    gene = fuc_gene()    # 直接调用或执行生成器函数,并不会执行其函数体,而是在内存中创建一个生成器函数对象
    print(gene.__next__()) # 通过调用生成器函数对象的__next__() 或者 send()方法来,来推动与之对应的生成器函数的陈函数体的执行。
    print(gene.__next__())

    列表推导式一些例子:

    有一个列表 :l1 = ['alex', 'WuSir', '老男孩', '太白']将其构造成这种列表['alex0', 'WuSir1', '老男孩2', '太白3']
    l1 = ['alex','WuSir','老男孩','大白3']
    dest4 = [e+str(index) for index,e in enumerate(l1)]
    print(dest4)
  • 相关阅读:
    mysql之四.表介绍
    mysql之三.mysql的工作流程
    mysql之二.mysql中的存储引擎
    mysql之一.初识mysql
    数据及表结构的导出
    迭代器和生成器
    python字符串格式化的几种方式
    关于global 和 nonlocal你需要注意的问题
    请编写一个函数实现将IP地址转换成一个整数
    Python中__repr__和__str__区别
  • 原文地址:https://www.cnblogs.com/chris-jia/p/9505045.html
Copyright © 2020-2023  润新知