zoukankan      html  css  js  c++  java
  • Python笔记003-生成器和生成器表达式

    Python笔记003-生成器和生成器表达式

    以下是我学习《流畅的Python》后的个人笔记,现在拿出来和大家共享,希望能帮到各位Python学习者。

    首次发表于: 微信公众号:科技老丁哥,ID: TechDing,敬请关注。

    本篇主要知识点:

    1. 生成器使用yield做关键字,一次只返回一个值给调用者,然后暂停执行,其作用是:节省内存空间。

    2. 生成器可以用next()函数,也可以用for迭代的方式获取元素值,中间还可以用close()来随时终止生成器。

    3. 生成器表达式可以认为是一种特殊的生成器,其代码更简洁,更容易理解,且和别的函数结合会更加灵活。

    1. 生成器

    生成器是Python中一个特殊的程序,用于控制循环的迭代行为。相对于一般函数用return来一次性返回所有值,生成器使用yield关键字,一次只返回一个值。

    这样的设计有很大的好处:在数据处理时,如果函数return出来的是一个非常大的数组,那么会非常占用内存,有时会报MemoryError的错误,而使用yield后一次仅仅返回一个元素值,可以优化内存占用的情况。

    从这种角度来讲,生成器函数每一次调用都返回一个元素值,这种特性使得生成器长得像函数,但行为却像迭代器。

    def squares(x): # 计算0-x的所有数的平方
    #     return [i*i for i in range(x)] # 普通写法,一次返回一个list,包含所有元素
        for i in range(x):
            yield i*i # 生成器:一次只返回一个值
    print(squares(5)) # <generator object squares at 0x00000157DBD16830>
    # 获取生成器中的元素值
    for value in squares(5): # 行为类似于迭代器,循环获取元素值
        print('value: ',value)
    

    生成器并不像一般的函数,它返回一个值后,生成器函数会自动挂起,等到下一次调用时(使用其内部成员方法__next__来实现),再返回到这个函数中继续执行。

    所以要想获取生成器的元素值,需要通过成员方法next()来进行,比如:

    square_five=squares(5)
    print(next(square_five)) # 0
    print(next(square_five)) # 1
    print(next(square_five)) # 4
    print(next(square_five)) # 9
    print(next(square_five)) # 16
    print(next(square_five)) # 报错:StopIteration: 超过yield的所有元素
    

    next()函数每次执行时,都会继续执行挂起的生成器函数,直到执行完毕。

    生成器的这种特点被称为"延迟计算"或"惰性求值(Lazy evaluation)",可以有效的节省内存。惰性求值实际上是体现了协同程序的思想。

    虽然生成器的这种行为类似于迭代器,但两者有较大差别,迭代器不具备这种执行-暂停-再执行-再暂停的特性,所以迭代器不具有延迟计算,没有协同程序的思想。

    使用延迟计算后,可以极大的节省内存,比如对大文件进行读取操作时,可以用下列生成器方法:

    ## 读取大文件的生成器方法:
    def load_big_file(file_path):
        BLOCK_SIZE = 1024
        with open(file_path, 'rb') as f:
            while True:
                block = f.read(BLOCK_SIZE)
                if block:
                    yield block # 一次只加载一个block到内存中,避免MemoryError
                else:
                    return
    

    生成器除了用next()函数来处理之外,还可以用close()来随时退出生成器。如下代码:

    ## 使用close()可以随时退出生成器
    square_five=squares(5)
    print(next(square_five)) # 0
    print(next(square_five)) # 1
    print(next(square_five)) # 4
    square_five.close() # 退出生成器
    print(next(square_five)) # Error: StopIteration:
    print(next(square_five)) # Error: StopIteration:
    

    2. 生成器表达式

    从形式上来看,生成器表达式和列表推导式很像,仅仅是将列表推导式中的[]替换为(),但是两者差别挺大,生成器表达式可以说组合了迭代功能和列表解析功能。

    生成器表达式可以认为是一种特殊的生成器函数,类似于lambda表达式和普通函数。但是和生成器一样,生成器表达式也是返回生成器generator对象,一次只返回一个值。

    # 上面的squares函数可以改写为:
    # 列表推导式的写法是:
    squares_list=[i*i for i in range(5)] # 一次性返回整个list
    print('列表推导式:',squares_list) # 列表推导式: [0, 1, 4, 9, 16]
    # 生成器表达式:
    squares2=(i*i for i in range(5)) # 生成器表达式一次返回一个值
    print('生成器表达式:',squares2) # 生成器表达式: <generator object ..
    print(next(squares2)) # 0
    print(next(squares2)) # 1
    print(next(squares2)) # 4
    

    生成器表达式是一种特殊的生成器,所以它也有生成器的特性,可以使用for循环来获取元素值,for循环内部自动调用了next()函数来执行。

    # generator对象可以直接用for来获取所有元素值
    squares2=(i*i for i in range(5)) # 生成器表达式就是一个generator对象
    for i in squares2:
        print('i: ',i)
    
    # 上面可以简写为:
    [print('i: ',i) for i in (i*i for i in range(5))]
    

    生成器表达式如果作为某个函数的参数,则可以省略掉(),直接使用即可,eg:

    ## 如果生成器表达式整个作为某个函数的参数,可以省略掉()
    max_value=max(i*i for i in range(5))  # 计算生成器的所有元素中的最大值
    print(max_value) # 16
    

    首次发表于: 微信公众号:科技老丁哥,ID: TechDing,敬请关注。

    本文所有代码都已经上传到我的github,欢迎下载

    参考资料:

    1. 《流畅的Python》,Luciano Ramalho (作者) 安道 , 吴珂 (译者)。
  • 相关阅读:
    toj 2819 Travel
    toj 2807 Number Sort
    zoj 2818 Prairie dogs IV
    zoj 1276 Optimal Array Multiplication Sequence
    toj 2802 Tom's Game
    toj 2798 Farey Sequence
    toj 2815 Searching Problem
    toj 2806 Replace Words
    toj 2794 Bus
    css截取字符
  • 原文地址:https://www.cnblogs.com/RayDean/p/10987551.html
Copyright © 2011-2022 走看看