如何在 Python 中将生成器表达式应用于任何可迭代对象

PythonPythonBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

Python 的生成器表达式提供了一种简洁高效的方式来处理可迭代对象。在本教程中,你将学习如何将生成器表达式应用于 Python 中的任何可迭代对象,从而获得性能提升和内存使用优化的好处。无论你是初学者还是经验丰富的 Python 开发者,本指南都将为你提供在项目中利用生成器表达式强大功能所需的知识。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/AdvancedTopicsGroup -.-> python/iterators("Iterators") python/AdvancedTopicsGroup -.-> python/generators("Generators") python/AdvancedTopicsGroup -.-> python/context_managers("Context Managers") subgraph Lab Skills python/iterators -.-> lab-397944{{"如何在 Python 中将生成器表达式应用于任何可迭代对象"}} python/generators -.-> lab-397944{{"如何在 Python 中将生成器表达式应用于任何可迭代对象"}} python/context_managers -.-> lab-397944{{"如何在 Python 中将生成器表达式应用于任何可迭代对象"}} end

理解生成器表达式

什么是生成器表达式?

生成器表达式,也称为生成器推导式,是在 Python 中创建生成器的一种简洁方式。它们类似于列表推导式,但不是创建一个列表,而是创建一个可以迭代的生成器对象。这使得它们在处理大型或无限数据集时比创建完整列表更节省内存。

生成器表达式的语法如下:

(expression for item in iterable)

列表推导式和生成器表达式之间的关键区别在于使用圆括号而不是方括号。这个细微的变化将推导式转换为生成器表达式,后者创建的是一个生成器对象而不是列表。

生成器表达式的优点

  1. 内存效率高:生成器表达式比其列表推导式的对应物消耗更少的内存,因为它们是即时生成值,而不是一次性将所有值存储在内存中。
  2. 延迟求值:生成器表达式使用延迟求值,这意味着它们只在需要时才生成值。在处理大型或无限数据集时,这可能特别有用,因为它避免了将整个数据集加载到内存中的需要。
  3. 链式操作:生成器表达式可以链接在一起,允许你在不创建中间列表的情况下对数据执行复杂的转换。
  4. 可读性强:生成器表达式可以使你的代码更具可读性和简洁性,因为它们允许你在一个紧凑的表达式中表达复杂的操作。

生成器表达式剖析

一个生成器表达式由三个主要部分组成:

  1. 表达式:将对可迭代对象中的每个项求值的表达式。
  2. 可迭代对象:生成器表达式将迭代的可迭代对象(例如列表、元组、集合或任何其他可迭代对象)。
  3. 条件(可选):一个可选的条件表达式,可用于过滤可迭代对象中的项。

下面是一个简单的生成器表达式示例,它对列表中的每个数字求平方:

squares = (x**2 for x in range(10))
for square in squares:
    print(square)

这将输出:

0
1
4
9
16
25
36
49
64
81

对可迭代对象运用生成器表达式

将生成器表达式应用于不同的可迭代对象

生成器表达式可应用于各种可迭代对象,包括列表、元组、集合、字典,甚至是自定义的可迭代类。这种灵活性使它们成为在 Python 中处理数据的强大工具。

以下是对不同类型可迭代对象使用生成器表达式的一些示例:

列表

## 对列表中的每个数字求平方
squares = (x**2 for x in [1, 2, 3, 4, 5])
print(list(squares))  ## 输出: [1, 4, 9, 16, 25]

## 从列表中过滤出偶数
even_numbers = (x for x in [1, 2, 3, 4, 5] if x % 2 == 0)
print(list(even_numbers))  ## 输出: [2, 4]

元组

## 将元组中的每个元素翻倍
doubled = (x * 2 for x in (1, 2, 3, 4, 5))
print(tuple(doubled))  ## 输出: (2, 4, 6, 8, 10)

字典

## 对字典中的键求平方
squared_keys = {x**2: v for x, v in {'a': 1, 'b': 2, 'c': 3}.items()}
print(squared_keys)  ## 输出: {1: 1, 4: 2, 9: 3}

## 反转字典中的键值对
reversed_dict = {v: k for k, v in {'a': 1, 'b': 2, 'c': 3}.items()}
print(reversed_dict)  ## 输出: {1: 'a', 2: 'b', 3: 'c'}

自定义可迭代对象

class MyIterable:
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return (x for x in self.data)

my_iterable = MyIterable([1, 2, 3, 4, 5])
squared = (x**2 for x in my_iterable)
print(list(squared))  ## 输出: [1, 4, 9, 16, 25]

在最后一个示例中,我们定义了一个自定义可迭代类 MyIterable,并使用生成器表达式对可迭代对象中的元素求平方。

链接生成器表达式

生成器表达式的强大功能之一是它们能够链接在一起,使你能够以简洁高效的方式对数据执行复杂的转换。

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

## 链接多个生成器表达式
doubled_evens = (x * 2 for x in (y for y in numbers if y % 2 == 0))
print(list(doubled_evens))  ## 输出: [4, 8, 12, 16, 20]

在这个示例中,我们首先过滤数字列表,只保留偶数,然后使用链接的生成器表达式将每个偶数翻倍。

使用生成器表达式优化性能

内存效率

使用生成器表达式的主要好处之一是其内存效率。与将所有生成的值存储在内存中的列表推导式不同,生成器表达式仅在需要时才生成值。这使得它们在处理大型或无限数据集时特别有用,因为将所有值存储在内存中可能不切实际甚至不可能。

为了展示生成器表达式的内存效率,让我们比较一下列表推导式和生成器表达式的内存使用情况:

import sys

## 列表推导式
numbers = [x for x in range(1000000)]
print(f"列表推导式内存使用情况: {sys.getsizeof(numbers)} 字节")

## 生成器表达式
squares = (x**2 for x in range(1000000))
print(f"生成器表达式内存使用情况: {sys.getsizeof(squares)} 字节")

输出:

列表推导式内存使用情况: 8000056 字节
生成器表达式内存使用情况: 112 字节

如你所见,生成器表达式使用的内存比列表推导式少得多,这使得它在处理大型数据集时是更高效的选择。

延迟求值

生成器表达式的另一个关键优势是它们使用延迟求值。这意味着值仅在需要时才生成,而不是一次性全部生成。在处理无限或非常大的数据集时,这可能特别有用,因为一次性生成所有值可能不切实际甚至不可能。

## 使用生成器表达式生成前 10 个平方数
squares = (x**2 for x in range(1000000))
for i in range(10):
    print(next(squares))

输出:

0
1
4
9
16
25
36
49
64
81

在这个例子中,我们只生成前 10 个平方数,而不是整个 1,000,000 个平方数。这是通过生成器表达式的延迟求值实现的。

链接生成器表达式

如前所述,生成器表达式的强大功能之一是它们能够链接在一起。这使你能够以简洁高效的方式对数据执行复杂的转换,而无需创建中间数据结构。

## 链接多个生成器表达式
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
doubled_evens = (x * 2 for x in (y for y in numbers if y % 2 == 0))
print(list(doubled_evens))  ## 输出: [4, 8, 12, 16, 20]

在这个例子中,我们首先过滤数字列表,只保留偶数,然后使用链接的生成器表达式将每个偶数翻倍。这种方法比创建中间列表或使用嵌套循环更节省内存。

通过理解并利用生成器表达式的性能优势,你可以编写更高效、可扩展的 Python 代码,尤其是在处理大型或无限数据集时。

总结

在本 Python 教程中,你已经学会了如何利用生成器表达式来处理任何可迭代对象,从而优化代码性能。通过理解生成器表达式的优势及其实际应用,你现在可以编写更高效、可扩展的 Python 程序了。拥抱生成器表达式的强大功能,将你的 Python 技能提升到新的水平。