首页 > 编程笔记 > Python笔记 阅读:11

Python生成器(Generator)的用法(附带实例)

Python 中创建一个列表的常用方法是首先定义一个列表变量,然后依次写出列表中的所有元素。

比如,创建一个包含 1~5 的所有整数的列表:
>>> list = [1,2,3,4,5]
>>> list
[1, 2, 3, 4, 5]

继续扩展,假设创建一个包含 1~10 的所有整数的列表,则使用这种方法就不得不将 10 个整数全部写出来,随着数据量的增大,编程工作量随之骤增。为了减少重复、烦琐的操作,Python 引入 range() 函数,使用 range(1, 11) 函数表示 1~10 的所有整数:
>>> list = range(1, 11)
>>> list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

再次改变需求,假设创建一个 1~10 的所有整数平方的列表,则首先想到的是使用 for 循环来实现:
>>> list = []
>>> for num in range(1, 11):
...     list.append(num * num)
...
>>> list
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
在程序中,首先定义了一个空列表 list,然后通过 for 循环遍历 1~10 的所有整数,将 10 个整数进行平方运算之后,使用 append() 函数添加到列表中。

虽然实现了预期的需求,但是需要通过多行代码才能实现,过程非常繁琐。Python 以代码量少、简洁、精炼著称。为了实现需求,Python 提供了高级特性,即列表推导式。

Python列表推导式

列表推导式(list comprehensions)是 Python 内置的、简单且强大、可以用来轻松创建列表(list)的方法,使用非常简单的语句利用其他列表创建新的列表,使用一行语句即可创建 1~10 的所有整数平方的列表:
>>> list = [num * num for num in range(1, 11)]
>>> list
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

列表推导式的书写规则是,生成的元素放在语句前面,紧跟着的是 for 循环。在列表推导式的循环后加上条件判断,可以创建 1~10 中所有偶数平方的列表:
>>> list = [num * num for num in range(1, 11) if num % 2 == 0]
>>> list
[4, 16, 36, 64, 100]

使用两个 for 循环即可将两个列表中的元素进行全排列,继而生成新的列表:
>>> list = [char + num for char in ['a', 'b', 'c'] for num in ['1', '2', '3']]
>>> list
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
运用列表推导式可以快速生成列表,且代码非常简洁。

Python生成器表达式

基于列表继续扩展,如果列表中的元素个数持续扩大,达到 10 万个,则如此大的列表将占用巨大的内存空间。假设程序实际上只需要访问列表中的几个元素,那么列表占用的绝大多数内存空间都是多余的,纯属浪费。

为了解决这个问题,Python 引入了生成器表达式。

可以通过生成器表达式将列表改为一个生成器。列表一旦被创建,所包含的元素就实实在在地存在内存空间了。列表存放的是元素。生成器存放的是算法。由于通过 next() 调用算法可实时生成元素,因此生成器占用的内存空间很小。

将列表推导式的方括号[]改为小括号(),即可创建一个生成器:
>>> list = [num * num for num in range(1, 6)]
>>> list
[1, 4, 9, 16, 25]
>>> g = (num * num for num in range(1, 6))
>>> g
<generator object <genexpr> at 0xb68fa25c>
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>
可以看到,列表 list 一旦被创建,内存空间就存放了所有元素,生成器 g 中的元素将随着 next() 函数的调用实时生成,直到最后没有元素可生成时,抛出 StopIteration 错误。

Python生成器函数

除了生成器表达式可以变为生成器,生成器函数同样可以变为生成器,编写一个函数,通过该函数可以计算任意自然数的平方数。
def square(input):
    list = []
    for num in range(input):
        list.append(num * num)
        print(list)
    return list

for num in square(5):
    print(num)
运行结果为:
[0]
[0, 1]
[0, 1, 4]
[0, 1, 4, 9]
[0, 1, 4, 9, 16]
0
1
4
9
16
可以看到,square() 函数在运行过程中会创建一个列表,若计算的自然数变大,列表也会变大,则程序会占用大量的内存空间,当自然数无限大时,程序将因无法分配到足够的内存空间而无法运行。

为了解决这个问题,借助生成器,将 square() 函数改为生成器函数,使用 yield 关键字:
def square(input):
    for num in range(input):
        print('before yield')
        yield num * num
        print('after yield')

for num in square(5):
    print(num)
运行结果为:
before yield
0
after yield
before yield
1
after yield
before yield
4
after yield
before yield
9
after yield
before yield
16
after yield
可以看到,使用生成器函数同样实现了与普通函数同样的功能,区别如下:

相关文章