在日常开发中,循环遍历是最基础也是最频繁的操作之一。很多开发者习惯了用for i in range(len(list))这种方式来遍历,却不知道Python提供了更优雅、更高效的写法。今天我们探讨5种循环遍历的常用写法,看看它们各自的适用场景和性能差异。
第一种:for-in直接遍历
这是Python中最基础也是最推荐的遍历方式。与其他语言不同,Python的for循环天生就是迭代器模式。
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
print(fruit)
这种写法简洁直观,不需要索引,代码可读性极强。更重要的是,它适用于所有可迭代对象,包括列表、元组、字符串、字典、集合等。
# 遍历字符串
for char in"Python":
print(char)
# 遍历字典的键
user = {'name': 'Alice', 'age': 25}
for key in user:
print(key, user[key])
# 遍历集合
tags = {'python', 'java', 'go'}
for tag in tags:
print(tag)
这种方式的底层原理是调用对象的__iter__()方法获取迭代器,然后不断调用__next__()方法获取元素,直到抛出StopIteration异常。这种机制使得遍历过程内存友好,即使是超大数据集也不会一次性加载到内存。
第二种:enumerate()获取索引和值
当我们既需要元素值又需要索引时,enumerate()是最优雅的选择。
languages = ['Python', 'Java', 'C++', 'Go']
for index, lang in enumerate(languages):
print(f"{index}: {lang}")
enumerate()还可以指定起始索引,这在某些场景下非常实用:
for index, lang in enumerate(languages, start=1):
print(f"第{index}名: {lang}")
在处理文件读取时,enumerate()特别有用:
with open('data.txt', 'r') as f:
for line_num, line in enumerate(f, start=1):
if'ERROR'in line:
print(f"第{line_num}行发现错误: {line.strip()}")
从性能角度看,enumerate()并不会创建额外的列表副本,它返回的是一个迭代器对象,在遍历过程中动态生成索引值对,因此内存开销极小。
第三种:zip()并行遍历多个序列
当需要同时遍历多个序列时,zip()函数可以将它们"拉链式"组合在一起。
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
cities = ['Beijing', 'Shanghai', 'Shenzhen']
for name, age, city in zip(names, ages, cities):
print(f"{name}, {age}岁, 来自{city}")
需要注意的是,zip()遵循"短板原则",即遍历长度由最短的序列决定:
list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c']
for num, char in zip(list1, list2):
print(num, char)
# 只会输出3对
如果需要以最长序列为准,可以使用itertools.zip_longest():
from itertools import zip_longest
for num, char in zip_longest(list1, list2, fillvalue='N/A'):
print(num, char)
zip()在数据处理场景中极其常用,比如合并两个CSV的列、构建字典等:
keys = ['name', 'age', 'city']
values = ['Alice', 25, 'Beijing']
user_dict = dict(zip(keys, values))
# {'name': 'Alice', 'age': 25, 'city': 'Beijing'}
第四种:字典的items()、keys()、values()遍历
字典作为Python中最重要的数据结构之一,提供了三种专门的遍历方法。
遍历键值对
user = {'name': 'Alice', 'age': 25, 'city': 'Beijing'}
# 最推荐的方式
for key, value in user.items():
print(f"{key}: {value}")
items()返回的是一个字典视图对象,而不是列表,这意味着它不会占用额外内存。在Python 3.7+版本中,字典保证了插入顺序,这让遍历结果更可预测。
仅遍历键或值
# 遍历键
for key in user.keys():
print(key)
# 遍历键的简写(推荐)
for key in user:
print(key)
# 遍历值
for value in user.values():
print(value)
在实际开发中,根据场景选择合适的方法可以提高代码效率。比如检查某个值是否存在时,用values()比items()更快:
if'Alice'in user.values():
print("找到了Alice")
嵌套字典的遍历
处理嵌套字典时,可以结合使用这些方法:
students = {
'class1': {'Alice': 90, 'Bob': 85},
'class2': {'Charlie': 92, 'David': 88}
}
for class_name, students_dict in students.items():
print(f"{class_name}:")
for student, score in students_dict.items():
print(f" {student}: {score}分")
第五种:列表推导式的变体写法
虽然列表推导式严格来说不算循环遍历,但它是一种非常Pythonic的处理数据方式,在很多场景下可以替代传统循环。
基础列表推导式
numbers = [1, 2, 3, 4, 5]
squares = [x**2for x in numbers]
# [1, 4, 9, 16, 25]
带条件过滤
# 筛选偶数并平方
even_squares = [x**2for x in numbers if x % 2 == 0]
# [4, 16]
嵌套推导式
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
字典和集合推导式
# 字典推导式
users = ['Alice', 'Bob', 'Charlie']
user_dict = {name: len(name) for name in users}
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}
# 集合推导式
numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x**2for x in numbers}
# {1, 4, 9, 16}
生成器表达式
当数据量较大时,生成器表达式比列表推导式更节省内存:
# 列表推导式:一次性生成所有元素
squares_list = [x**2for x in range(1000000)]
# 生成器表达式:按需生成
squares_gen = (x**2for x in range(1000000))
# 使用生成器
for square in squares_gen:
if square > 100:
break
生成器的优势在于惰性求值,只有在需要时才计算下一个值,这在处理大数据集或无限序列时尤为重要。
性能对比与选择建议
不同的遍历方式在性能上有微小差异。我做了一个简单的性能测试:
import timeit
data = list(range(100000))
# 方式1:直接遍历
deftest1():
result = []
for item in data:
result.append(item * 2)
return result
# 方式2:使用enumerate
deftest2():
result = []
for i, item in enumerate(data):
result.append(item * 2)
return result
# 方式3:列表推导式
deftest3():
return [item * 2for item in data]
print(f"直接遍历: {timeit.timeit(test1, number=100):.4f}秒")
print(f"enumerate: {timeit.timeit(test2, number=100):.4f}秒")
print(f"列表推导式: {timeit.timeit(test3, number=100):.4f}秒")
通常情况下,列表推导式性能最优,因为它在C层面做了优化。但选择遍历方式不应该只看性能,更重要的是代码可读性和维护性。
选择建议:
- 只需要元素值 → 用
for item in list - 遍历字典 → 用
items()、keys()、values()
写在最后
Python的循环遍历远不止这5种写法,还有iter()配合next()的手动迭代、while循环、itertools模块提供的各种高级迭代工具等。
编写优雅的Python代码,不仅是追求性能,更是追求代码的清晰表达。当你的代码让后来的维护者一眼就能看懂你的意图时,那才是真正的"Pythonic"。