• 生成器高级用法


    生成器高级用法

    1.概述

    这篇文章介绍生成器的一些使用技巧,同时用它来替代传统容器实现一些好用的功能。

    2.生成器

    2.1.生成器介绍

    生成器是python里的一种特殊的数据类型,他是一个不断给调用方生成内容的类型。定义一个生成器需要用到生成器函数与yield关键字。

    1.创建一个生成器Demo
    # 生成器
    def generate_even(max_number):
    	# 一个简单的生成器,返回0到max_number之间所有的偶数
        for i in range(0, max_number):
            if i % 2 == 0:
                yield i
    
    # 遍历生成器对象,输出结果
    for i in generate_even(100):
        print(i)
    
    # 调用next()函数可以逐步从生成器里拿到结果
    g = generate_even(10)
    print(next(g))
    print(next(g))
    print(next(g))
    
    # 因为生成器是可迭代对象,所以可以使用容器内置函数将其转为其他容器类型。例如将它转为list容器
    mylist = list(generate_even(10))
    print(f'将生成器转换为list容器:type({mylist})'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2.按需返回替代容器

    Python2时代,如果用range()生成一个非常大的数字序列,速度会非常慢。这是因为range()需要组装并返回一个巨大的列表。整个计算与内存分配会耗费大量时间。

    # 在python2中的range()会将结果组装的一个列表,一次性返回所有数字。
    >>> range(4)
    [0, 1, 2, 3]
    
    • 1
    • 2
    • 3

    在python3时代,调用range()瞬间就会返回结果,因为它不在返回列表,而是返回一个类型为range的惰性计算对象。
    当序列过大时,新的range()函数不再会一次性耗费大量内存和时间,生成一个巨大的列表,而是仅在迭代时按需返回数字。
    range()的进化虽然简单,但它代表了一个重要的编程思维——按需生成,而不是一次性返回

    虽然都是返回结果,但yield和return的最大不同之处在于,return的返回是一次性的,使用它会直接终止整个函数执行。而yield可以逐步给调用方生成结果。

    # r 是range对象,而非装满数字的列表
    >>> r = range(10000000)
    # 只有在迭代range对象时,他才会不断生成新的数字
    >>> for i in r:
    >>> 	print(i)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.生成器应用案例

    3.1.用生成器替代列表

    在写代码过程中,我们经常要处理一些数据,假如在批处理时数据量过大或者处理逻辑复杂导致处理速度很慢,例如下面的示例存在两点问题:

    • 将处理后的大数据放到列表中,一次性返回占用很大的内存,同时处理速度也很慢。
    • 如果函数调用方想在满足特定条件时中断,不在继续处理后面的数据,现在batch_process函数是做不到的。
    def batch_process(items):
        '''
        批量多个items对象
        :param items:
        :return:
        '''
        result = []
        for item in range(0, items):
            # 假如处理item需要花费大量时间
            if item % 2 == 0:
                result.append(item)
        # 处理的结果拼接到列表中返回
        return result
        
    process = batch_process(10)
    print(process)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    为了解决上面的问题,我们可以使用生成器改写它,用yield替代列表。

    def batch_yield(items):
        '''
            批量多个items对象
            :param items:
            :return:
        '''
        for item in range(0, items):
            # 假如处理item需要花费大量时间
            if item % 2 == 0:
                yield item
    
    
    for i in batch_yield(10):
        # 满足特定条件后不在处理后面的数据
        if i == 4:
            break
        print(i)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    Sentinel 面试题及答案整理,最新面试题
    【Echarts】问题汇总
    【Linux】进程间通信之消息队列
    前端页面基础HTML、CSS、JS学习Day01
    前端常用的状态码
    java存储数据到本地txt文件中-以及-读取txt文件的内容
    工程机械——起重机导电滑环
    Hugging Face Accelerate 两个后端的故事:FSDP 与 DeepSpeed
    固定资产管理口号标语怎么写
    什么是Docker容器?Docker容器和VM有什么区别?
  • 原文地址:https://blog.csdn.net/m0_38039437/article/details/126567213