目录
在Python编程中,选择正确的数据结构往往比优化算法本身更能提升程序性能。本文将通过五个完整、可运行的代码实例,带你深入理解Python中最具代表性的数据结构:从基础的列表推导式到高级的双端队列,再到不可变的具名元组。每个示例都配有详细注释,旨在解决实际开发中的常见问题。
示例一:列表推导式——数据清洗与转换
列表是Python中最常用的数据结构,但传统的for循环往往显得冗长。列表推导式提供了一种更简洁、更Pythonic的方式来创建和过滤列表。本例模拟了一个常见的数据清洗场景:从原始数据中筛选出有效的数字并进行平方运算。
# -*- coding: utf-8 -*-
"""
列表推导式实战:数据清洗与转换
场景:假设我们有一组混合数据(包含字符串和数字),需要筛选出偶数并计算其平方。
"""
# 原始数据,模拟从外部接口获取的脏数据
raw_data = [1, 'a', 4, 7, 8, None, 10, 15, 20]
# 使用列表推导式进行数据清洗和转换
# 1. 检查元素是否为整数 (isinstance(item, int))
# 2. 检查是否为偶数 (item % 2 == 0)
# 3. 计算平方 (item ** 2)
cleaned_squares = [item ** 2 for item in raw_data
if isinstance(item, int) and item % 2 == 0]
print("原始数据:", raw_data)
print("清洗并平方后的数据:", cleaned_squares)
# 输出: [16, 64, 100, 400]
示例二:defaultdict——告别KeyError
字典是存储键值对的利器,但在处理分组或计数时,原生字典需要繁琐的键存在性检查。collections.defaultdict可以自动初始化缺失的键,让代码更加流畅。本例演示如何将学生按成绩等级分组。
# -*- coding: utf-8 -*-
"""
defaultdict实战:自动分组
场景:将学生列表按照成绩等级(A, B, C)进行分组。
"""
from collections import defaultdict
# 学生数据:姓名 -> 分数
students_scores = {
'Alice': 95,
'Bob': 85,
'Charlie': 75,
'David': 90,
'Eve': 80
}
# 创建一个默认值为列表的字典
# 当访问不存在的键时,自动创建一个空列表
grade_groups = defaultdict(list)
# 遍历学生数据,根据分数划分等级并分组
for name, score in students_scores.items():
if score >= 90:
grade_groups['A'].append(name)
elif score >= 80:
grade_groups['B'].append(name)
else:
grade_groups['C'].append(name)
print("各等级学生分组:")
for grade, students in grade_groups.items():
print(f"等级 {grade}: {students}")
示例三:Counter——数据统计神器
在数据分析中,统计元素频次是高频需求。collections.Counter将这一过程极度简化。本例演示如何分析一段文本中单词的出现频率。
# -*- coding: utf-8 -*-
"""
Counter实战:词频统计
场景:分析一段英文文本中单词的出现频率。
"""
from collections import Counter
import re
# 示例文本
text = """
Python is a great programming language. Python is easy to learn and Python is powerful.
Many developers love Python because Python makes coding fun.
"""
# 1. 使用正则表达式提取单词,并转换为小写
words = re.findall(r'\b[a-zA-Z]+\b', text.lower())
# 2. 使用Counter进行统计
word_counter = Counter(words)
# 3. 输出最常见的5个单词
print("单词频率统计 (前5名):")
for word, freq in word_counter.most_common(5):
print(f"{word}: {freq}")
# 4. Counter支持数学运算,演示如何合并两个统计结果
another_text = "Python is awesome. I love coding in Python."
another_words = re.findall(r'\b[a-zA-Z]+\b', another_text.lower())
another_counter = Counter(another_words)
# 合并统计
total_counter = word_counter + another_counter
print(f"\n合并后的Python出现次数: {total_counter['python']}")
示例四:deque——高效的队列操作
当需要频繁在序列两端进行插入和删除时,列表的性能会急剧下降。collections.deque(双端队列)为此而生,它在两端操作的时间复杂度均为O(1)。本例模拟一个简单的浏览器历史记录管理器。
# -*- coding: utf-8 -*-
"""
deque实战:双端队列
场景:模拟浏览器的前进/后退功能(有限大小的历史记录)。
"""
from collections import deque
class BrowserHistory:
def __init__(self, maxlen=5):
# maxlen限制历史记录的最大数量,自动弹出最老的记录
self.history = deque(maxlen=maxlen)
self.current = None
def visit(self, page):
if self.current:
self.history.append(self.current)
self.current = page
print(f"访问了: {page}")
def back(self):
if self.history:
# 从历史记录中取出上一个页面
last_page = self.history.pop()
# 将当前页面压入历史,以便将来可以前进
if self.current:
self.history.appendleft(self.current)
self.current = last_page
print(f"后退到: {last_page}")
else:
print("已经是最早的页面了")
# 使用示例
browser = BrowserHistory(maxlen=3)
browser.visit("Google")
browser.visit("GitHub")
browser.visit("StackOverflow")
browser.back() # 后退
示例五:namedtuple——带字段名的不可变数据
当需要表示一个简单的数据记录(如坐标、学生信息)且不希望数据被修改时,namedtuple是轻量级且优雅的选择。它比字典更节省内存,比普通元组更具可读性。
# -*- coding: utf-8 -*-
"""
namedtuple实战:不可变数据记录
场景:定义一个学生数据结构,并进行操作。
"""
from collections import namedtuple
# 1. 定义一个具名元组
# 第一个参数是类名,第二个参数是字段名字符串
Student = namedtuple('Student', 'name age grade subject')
# 2. 创建实例
student1 = Student(name='张三', age=20, grade=95, subject='Math')
student2 = Student('李四', 21, 88, 'Physics') # 也可以按位置传参
# 3. 访问数据(既可以通过点号,也可以通过索引)
print(f"学生姓名: {student1.name}")
print(f"年龄: {student1[1]}") # 兼容普通元组的索引访问
# 4. 不可变性测试
try:
student1.age = 21 # 这会抛出AttributeError
except AttributeError as e:
print("错误:", e, "(具名元组是不可变的)")
# 5. _replace方法创建新实例
# 虽然不能修改原实例,但可以基于原实例创建一个修改了某些字段的新实例
updated_student = student1._replace(grade=98)
print(f"更新后的成绩: {updated_student}")
总结
通过以上五个实例,我们可以看到:
- 列表推导式让数据转换代码量减少50%以上。
- defaultdict消除了繁琐的键存在性检查,让逻辑更清晰。
- Counter将复杂的统计循环简化为一行代码。
- deque在处理两端操作时提供了极致的性能。
- namedtuple在需要轻量级、不可变对象时是字典和类的最佳折中。
在实际开发中,根据具体场景选择最合适的数据结构,是写出高效、优雅Python代码的关键。建议读者运行并修改上述代码,加深对这些数据结构的理解。
本文由 尚先生 原创,转载请注明出处。
评论
0