乐闻世界logo
搜索文章和话题

Python 中的列表推导式和生成器表达式有什么区别?

2月21日 17:10

Python 生成器表达式与列表推导式详解

列表推导式

基本语法

列表推导式是一种简洁的创建列表的方式,它将循环和条件判断结合在一起。

python
# 基本列表推导式 numbers = [1, 2, 3, 4, 5] # 传统方式 squares = [] for num in numbers: squares.append(num ** 2) # 列表推导式 squares = [num ** 2 for num in numbers] print(squares) # [1, 4, 9, 16, 25]

带条件的列表推导式

python
# 带过滤条件的列表推导式 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 获取偶数 evens = [num for num in numbers if num % 2 == 0] print(evens) # [2, 4, 6, 8, 10] # 获取大于5的奇数 odd_gt_5 = [num for num in numbers if num % 2 == 1 and num > 5] print(odd_gt_5) # [7, 9] # 使用 if-else 表达式 result = ["偶数" if num % 2 == 0 else "奇数" for num in numbers[:5]] print(result) # ['奇数', '偶数', '奇数', '偶数', '奇数']

嵌套列表推导式

python
# 嵌套列表推导式 matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # 展平二维列表 flattened = [item for row in matrix for item in row] print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9] # 转置矩阵 transposed = [[row[i] for row in matrix] for i in range(3)] print(transposed) # [[1, 4, 7], [2, 5, 8], [3, 6, 9]] # 创建乘法表 multiplication_table = [[i * j for j in range(1, 6)] for i in range(1, 6)] for row in multiplication_table: print(row) # [1, 2, 3, 4, 5] # [2, 4, 6, 8, 10] # [3, 6, 9, 12, 15] # [4, 8, 12, 16, 20] # [5, 10, 15, 20, 25]

列表推导式的实际应用

python
# 1. 数据转换 names = ["alice", "bob", "charlie"] capitalized = [name.capitalize() for name in names] print(capitalized) # ['Alice', 'Bob', 'Charlie'] # 2. 数据过滤 data = [1, -2, 3, -4, 5, -6, 7, -8, 9, -10] positive = [x for x in data if x > 0] print(positive) # [1, 3, 5, 7, 9] # 3. 字典键值转换 user_dict = {"name": "Alice", "age": 25, "city": "New York"} keys = [key for key in user_dict.keys()] values = [value for value in user_dict.values()] items = [f"{key}: {value}" for key, value in user_dict.items()] print(keys) # ['name', 'age', 'city'] print(values) # ['Alice', 25, 'New York'] print(items) # ['name: Alice', 'age: 25', 'city: New York'] # 4. 文件处理 # 假设有一个文件包含多行文本 lines = ["hello world", "python is great", "list comprehension"] words = [word for line in lines for word in line.split()] print(words) # ['hello', 'world', 'python', 'is', 'great', 'list', 'comprehension']

生成器表达式

基本语法

生成器表达式与列表推导式语法相似,但使用圆括号而不是方括号。生成器表达式返回一个生成器对象,而不是列表。

python
# 生成器表达式 numbers = [1, 2, 3, 4, 5] # 列表推导式 squares_list = [num ** 2 for num in numbers] print(squares_list) # [1, 4, 9, 16, 25] print(type(squares_list)) # <class 'list'> # 生成器表达式 squares_gen = (num ** 2 for num in numbers) print(squares_gen) # <generator object <genexpr> at 0x...> print(type(squares_gen)) # <class 'generator'> # 使用生成器 print(list(squares_gen)) # [1, 4, 9, 16, 25]

生成器的惰性求值

python
# 生成器的惰性求值特性 def count(): print("生成器开始执行") for i in range(5): print(f"生成 {i}") yield i # 创建生成器 gen = count() print("生成器已创建") # 逐个获取值 print(f"获取值: {next(gen)}") print(f"获取值: {next(gen)}") print(f"获取值: {next(gen)}") # 输出: # 生成器已创建 # 生成器开始执行 # 生成 0 # 获取值: 0 # 生成 1 # 获取值: 1 # 生成 2 # 获取值: 2

生成器表达式的实际应用

python
# 1. 处理大文件 # 假设有一个大文件,逐行读取 def read_large_file(filename): with open(filename, 'r') as f: for line in f: yield line.strip() # 使用生成器表达式处理 # lines = (line for line in read_large_file('large_file.txt')) # long_lines = [line for line in lines if len(line) > 100] # 2. 无限序列 import itertools # 无限的偶数生成器 evens = (i for i in itertools.count(0, 2)) print(next(evens)) # 0 print(next(evens)) # 2 print(next(evens)) # 4 # 3. 链式处理 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 链式生成器表达式 result = ( num ** 2 for num in numbers if num % 2 == 0 if num > 4 ) print(list(result)) # [36, 64, 100] # 4. 内存高效的数据处理 # 处理大量数据时,使用生成器可以节省内存 large_data = range(1000000) # 列表推导式 - 占用大量内存 # squares_list = [x ** 2 for x in large_data] # 生成器表达式 - 内存高效 squares_gen = (x ** 2 for x in large_data) # 只在需要时计算 print(next(squares_gen)) # 0 print(next(squares_gen)) # 1

列表推导式 vs 生成器表达式

内存使用对比

python
import sys # 列表推导式 - 立即创建所有元素 list_comp = [x ** 2 for x in range(1000000)] print(f"列表推导式内存使用: {sys.getsizeof(list_comp)} bytes") # 生成器表达式 - 惰性求值,不立即创建所有元素 gen_expr = (x ** 2 for x in range(1000000)) print(f"生成器表达式内存使用: {sys.getsizeof(gen_expr)} bytes") # 输出: # 列表推导式内存使用: 8000056 bytes (约 8MB) # 生成器表达式内存使用: 200 bytes (非常小)

性能对比

python
import time # 测试列表推导式性能 start = time.time() list_comp = [x ** 2 for x in range(1000000)] list_time = time.time() - start # 测试生成器表达式性能 start = time.time() gen_expr = (x ** 2 for x in range(1000000)) gen_time = time.time() - start print(f"列表推导式创建时间: {list_time:.4f} 秒") print(f"生成器表达式创建时间: {gen_time:.4f} 秒") # 但如果需要遍历所有元素 start = time.time() for _ in list_comp: pass list_iterate_time = time.time() - start start = time.time() for _ in gen_expr: pass gen_iterate_time = time.time() - start print(f"列表推导式遍历时间: {list_iterate_time:.4f} 秒") print(f"生成器表达式遍历时间: {gen_iterate_time:.4f} 秒")

使用场景对比

python
# 适合使用列表推导式的场景 # 1. 需要多次访问结果 numbers = [1, 2, 3, 4, 5] squares = [x ** 2 for x in numbers] print(squares[0]) # 1 print(squares[2]) # 9 print(squares[4]) # 25 # 2. 需要索引访问 for i, value in enumerate(squares): print(f"索引 {i}: {value}") # 3. 需要切片操作 print(squares[1:4]) # [4, 9, 16] # 适合使用生成器表达式的场景 # 1. 处理大数据集 large_numbers = range(10000000) squares_gen = (x ** 2 for x in large_numbers) # 2. 只需要遍历一次 total = sum(x ** 2 for x in range(1000000)) print(f"总和: {total}") # 3. 链式操作 result = ( x ** 2 for x in range(100) if x % 2 == 0 ) result = (x + 1 for x in result) result = (x * 2 for x in result) print(list(result)[:5]) # [2, 18, 50, 98, 162]

高级应用

1. 使用生成器表达式实现管道

python
# 数据处理管道 def pipeline(data, *functions): """创建数据处理管道""" result = data for func in functions: result = func(result) return result # 定义处理函数 def filter_even(numbers): return (num for num in numbers if num % 2 == 0) def square(numbers): return (num ** 2 for num in numbers) def add_one(numbers): return (num + 1 for num in numbers) # 使用管道 numbers = range(10) result = pipeline(numbers, filter_even, square, add_one) print(list(result)) # [1, 5, 17, 37, 65]

2. 使用生成器表达式处理文件

python
# 假设有一个日志文件 # log.txt: # 2024-01-01 10:00:00 INFO User logged in # 2024-01-01 10:01:00 ERROR Connection failed # 2024-01-01 10:02:00 INFO User logged out # 2024-01-01 10:03:00 ERROR Timeout occurred # 使用生成器表达式处理日志文件 def process_log_file(filename): """处理日志文件,提取错误信息""" with open(filename, 'r') as f: # 生成器表达式:只提取错误行 errors = ( line.strip() for line in f if 'ERROR' in line ) # 进一步处理 error_messages = ( line.split('ERROR ')[1] for line in errors ) return list(error_messages) # errors = process_log_file('log.txt') # print(errors) # ['Connection failed', 'Timeout occurred']

3. 使用列表推导式创建复杂结构

python
# 创建字典 keys = ['name', 'age', 'city'] values = ['Alice', 25, 'New York'] person_dict = {keys[i]: values[i] for i in range(len(keys))} print(person_dict) # {'name': 'Alice', 'age': 25, 'city': 'New York'} # 创建嵌套字典 users = [ {'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}, {'name': 'Charlie', 'age': 35} ] user_index = {user['name']: user['age'] for user in users} print(user_index) # {'Alice': 25, 'Bob': 30, 'Charlie': 35} # 创建集合 numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] unique_numbers = {num for num in numbers} print(unique_numbers) # {1, 2, 3, 4} # 创建元组 coordinates = [(x, y) for x in range(3) for y in range(3)] print(coordinates) # [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

4. 使用生成器表达式实现无限序列

python
# 斐波那契数列生成器 def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b # 获取前10个斐波那契数 fib = fibonacci() first_10 = [next(fib) for _ in range(10)] print(first_10) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] # 质数生成器 def primes(): """生成质数""" num = 2 while True: if all(num % i != 0 for i in range(2, int(num ** 0.5) + 1)): yield num num += 1 # 获取前10个质数 prime_gen = primes() first_10_primes = [next(prime_gen) for _ in range(10)] print(first_10_primes) # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

最佳实践

1. 可读性优先

python
# 好的做法 - 清晰易读 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = [num for num in numbers if num % 2 == 0] # 不好的做法 - 过于复杂 result = [x for x in [y ** 2 for y in range(10)] if x > 50 and x < 100] # 更好的做法 - 分步骤 squares = [y ** 2 for y in range(10)] result = [x for x in squares if 50 < x < 100]

2. 避免副作用

python
# 不好的做法 - 在推导式中有副作用 items = [] result = [items.append(x) for x in range(10)] # 错误! # 好的做法 - 使用循环 items = [] for x in range(10): items.append(x) # 或者直接使用列表推导式 items = [x for x in range(10)]

3. 选择合适的数据结构

python
# 需要多次访问 - 使用列表 numbers = [x ** 2 for x in range(100)] print(numbers[0]) print(numbers[50]) print(numbers[99]) # 只需要遍历一次 - 使用生成器 total = sum(x ** 2 for x in range(1000000)) # 需要唯一值 - 使用集合 unique = {x % 10 for x in range(100)} # 需要键值对 - 使用字典 mapping = {x: x ** 2 for x in range(10)}

4. 考虑性能

python
# 对于大数据集,使用生成器表达式 large_data = range(10000000) # 好的做法 - 使用生成器 result = sum(x ** 2 for x in large_data) # 不好的做法 - 使用列表(占用大量内存) # result = sum([x ** 2 for x in large_data]) # 对于小数据集,列表推导式可能更快 small_data = range(100) result = sum([x ** 2 for x in small_data])

总结

Python 列表推导式和生成器表达式的核心概念:

列表推导式

  1. 基本语法[expression for item in iterable if condition]
  2. 特点:立即创建列表,支持索引和切片
  3. 适用场景:需要多次访问、需要索引操作、数据量较小

生成器表达式

  1. 基本语法(expression for item in iterable if condition)
  2. 特点:惰性求值,内存高效,只能遍历一次
  3. 适用场景:处理大数据集、只需要遍历一次、链式操作

主要区别

  1. 内存使用:生成器表达式更节省内存
  2. 访问方式:列表支持索引,生成器不支持
  3. 重用性:列表可以多次访问,生成器只能遍历一次
  4. 创建时间:生成器创建更快,但遍历时间可能更长

最佳实践

  1. 优先考虑可读性
  2. 避免在推导式中使用副作用
  3. 根据需求选择合适的数据结构
  4. 考虑性能和内存使用

掌握列表推导式和生成器表达式,能够编写出更简洁、更高效的 Python 代码。它们是 Python 中非常强大的特性,能够显著提高代码的可读性和性能。

标签:Python