目录
专栏导读
🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手
🏳️🌈 个人博客主页:请点击——> 个人的博客主页 求收藏
🏳️🌈 Github主页:请点击——> Github主页 求Star⭐
🏳️🌈 知乎主页:请点击——> 知乎主页 求关注
🏳️🌈 CSDN博客主页:请点击——> CSDN的博客主页 求关注
👍 该系列文章专栏:请点击——>Python办公自动化专栏 求订阅
🕷 此外还有爬虫专栏:请点击——>Python爬虫基础专栏 求订阅
📕 此外还有python基础专栏:请点击——>Python基础学习专栏 求订阅
文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
❤️ 欢迎各位佬关注! ❤️
前言
字典(dict)和集合(set)是 Python 中基于哈希表实现的两种重要数据结构。它们提供了快速的查找、插入和删除操作,是构建高效 Python 程序的关键工具。本文将深入探讨它们的内部机制、性能特性和使用技巧。
字典:键值对的魔法
字典基础
字典是 Python 中最重要的数据结构之一,用于存储键值对:
# 创建字典的多种方式
empty_dict = {}
person = {"name": "张三", "age": 25, "city": "北京"}
numbers = dict(one=1, two=2, three=3)
pairs = dict([("a", 1), ("b", 2), ("c", 3)])
zip_dict = dict(zip(["x", "y", "z"], [10, 20, 30]))
print(f"空字典: {empty_dict}")
print(f"人员信息: {person}")
print(f"数字: {numbers}")
print(f"键值对: {pairs}")
print(f"zip创建: {zip_dict}")
# 字典推导式
squares = {x: x**2 for x in range(5)}
print(f"平方数: {squares}")
字典的基本操作
# 访问和修改
student = {"name": "李四", "age": 20, "grades": [85, 90, 88]}
# 访问值
print(f"姓名: {student['name']}")
print(f"年龄: {student.get('age')}")
print(f"地址: {student.get('address', '未知')}") # 使用默认值
# 修改值
student["age"] = 21
student["grades"].append(92)
print(f"更新后: {student}")
# 添加新键值对
student["email"] = "lisi@example.com"
student.update({"phone": "13812345678", "major": "计算机科学"})
print(f"添加后: {student}")
# 删除键值对
del student["phone"]
email = student.pop("email")
print(f"删除后: {student}, 删除的邮箱: {email}")
字典的遍历
# 字典遍历的多种方式
scores = {"语文": 85, "数学": 92, "英语": 88, "物理": 90}
# 遍历键
for subject in scores:
print(f"科目: {subject}")
# 遍历值
for score in scores.values():
print(f"分数: {score}")
# 遍历键值对
for subject, score in scores.items():
print(f"{subject}: {score}分")
# 带索引的遍历
for i, (subject, score) in enumerate(scores.items()):
print(f"{i+1}. {subject}: {score}分")
# 计算平均分
average = sum(scores.values()) / len(scores)
print(f"平均分: {average:.2f}")
集合:唯一值的集合
集合基础
集合是存储唯一元素的无序集合:
# 创建集合
empty_set = set()
numbers = {1, 2, 3, 4, 5}
string_set = set("hello") # {'h', 'e', 'l', 'o'}
list_set = set([1, 2, 2, 3, 3, 4]) # {1, 2, 3, 4}
print(f"空集合: {empty_set}")
print(f"数字集合: {numbers}")
print(f"字符串集合: {string_set}")
print(f"列表转集合: {list_set}")
# 集合推导式
even_squares = {x**2 for x in range(10) if x % 2 == 0}
print(f"偶数平方: {even_squares}")
集合的操作
# 基本集合操作
set_a = {1, 2, 3, 4, 5}
set_b = {4, 5, 6, 7, 8}
# 添加和删除元素
set_a.add(6)
set_a.discard(2) # 安全删除(不存在不报错)
set_a.remove(3) # 删除(不存在会报错)
popped = set_a.pop() # 随机删除并返回一个元素
print(f"操作后: {set_a}, 弹出的元素: {popped}")
# 集合运算
union = set_a | set_b # 并集
intersection = set_a & set_b # 交集
difference = set_a - set_b # 差集
symmetric_diff = set_a ^ set_b # 对称差集
print(f"A: {set_a}")
print(f"B: {set_b}")
print(f"并集: {union}")
print(f"交集: {intersection}")
print(f"差集: {difference}")
print(f"对称差集: {symmetric_diff}")
# 集合关系
subset = {1, 2}.issubset({1, 2, 3})
superset = {1, 2, 3}.issuperset({1, 2})
disjoint = {1, 2}.isdisjoint({3, 4})
print(f"子集: {subset}")
print(f"超集: {superset}")
print(f"不相交: {disjoint}")
哈希表的内部机制
哈希原理
字典和集合的高效性来自于哈希表的实现:
# 查看对象的哈希值
print(f"整数 42 的哈希: {hash(42)}")
print(f"浮点数 3.14 的哈希: {hash(3.14)}")
print(f"字符串 'hello' 的哈希: {hash('hello')}")
# 不可变对象才有哈希值
try:
print(f"列表的哈希: {hash([1, 2, 3])}")
except TypeError as e:
print(f"列表没有哈希值: {e}")
# 元组有哈希值(如果元素都是不可变的)
tuple_hash = hash((1, 2, 3))
print(f"元组的哈希: {tuple_hash}")
# 嵌套元组
try:
nested_hash = hash((1, [2, 3]))
except TypeError as e:
print(f"包含列表的元组没有哈希值: {e}")
哈希冲突处理
Python 使用开放寻址法处理哈希冲突:
# 演示哈希冲突的概念
class SimpleHashTable:
"""简单的哈希表实现,演示冲突处理"""
def __init__(self, size=8):
self.size = size
self.table = [None] * size
self.count = 0
def _hash(self, key):
"""简单的哈希函数"""
return hash(key) % self.size
def _probe(self, index, i):
"""线性探测"""
return (index + i) % self.size
def insert(self, key, value):
"""插入键值对"""
if self.count >= self.size * 0.7: # 负载因子超过0.7就扩容
self._resize()
index = self._hash(key)
i = 0
while self.table[index] is not None and self.table[index][0] != key:
i += 1
index = self._probe(index, i)
if i >= self.size: # 避免无限循环
raise RuntimeError("哈希表已满")
if self.table[index] is None:
self.count += 1
self.table[index] = (key, value)
def get(self, key):
"""获取值"""
index = self._hash(key)
i = 0
while self.table[index] is not None:
if self.table[index][0] == key:
return self.table[index][1]
i += 1
index = self._probe(index, i)
if i >= self.size:
break
raise KeyError(f"键 {key} 不存在")
def _resize(self):
"""扩容"""
old_table = self.table
self.size *= 2
self.table = [None] * self.size
self.count = 0
for item in old_table:
if item is not None:
self.insert(item[0], item[1])
def display(self):
"""显示哈希表内容"""
print("哈希表内容:")
for i, item in enumerate(self.table):
if item is not None:
print(f" 槽位 {i}: {item[0]} -> {item[1]}")
else:
print(f" 槽位 {i}: 空")
# 测试简单哈希表
hash_table = SimpleHashTable(size=8)
hash_table.insert("name", "张三")
hash_table.insert("age", 25)
hash_table.insert("city", "北京")
hash_table.insert("job", "程序员")
hash_table.display()
print(f"获取 name: {hash_table.get('name')}")
print(f"获取 age: {hash_table.get('age')}")
字典的高级用法
1. 默认值处理
# 使用 setdefault() 处理默认值
word_count = {}
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
for word in words:
word_count.setdefault(word, 0)
word_count[word] += 1
print(f"单词计数: {word_count}")
# 使用 defaultdict(需要导入 collections)
from collections import defaultdict
word_count2 = defaultdict(int) # 默认值为 0
for word in words:
word_count2[word] += 1 # 自动初始化为 0
print(f"defaultdict 计数: {dict(word_count2)}")
# 嵌套的 defaultdict
def nested_defaultdict():
return defaultdict(list)
nested = defaultdict(nested_defaultdict)
nested["fruits"]["sweet"].append("apple")
nested["fruits"]["sweet"].append("banana")
nested["vegetables"]["green"].append("spinach")
print(f"嵌套结构: {dict(nested)}")
2. 字典合并
# Python 3.9+ 的合并操作符
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
# 合并(后面的字典优先级高)
merged = dict1 | dict2
print(f"合并结果: {merged}") # {'a': 1, 'b': 3, 'c': 4}
# 原地合并
dict1 |= dict2
print(f"原地合并: {dict1}")
# 早期版本的合并方法
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
# 方法1:使用 update()
merged1 = dict1.copy()
merged1.update(dict2)
print(f"update 合并: {merged1}")
# 方法2:使用解包操作符
merged2 = {**dict1, **dict2}
print(f"解包合并: {merged2}")
# 方法3:使用 ChainMap(保留多个字典的引用)
from collections import ChainMap
chain = ChainMap(dict2, dict1)
print(f"ChainMap: {dict(chain)}")
3. 字典排序
# 按值排序
scores = {"张三": 85, "李四": 92, "王五": 78, "赵六": 88}
# 按分数降序排序
sorted_by_score = dict(sorted(scores.items(), key=lambda x: x[1], reverse=True))
print(f"按分数排序: {sorted_by_score}")
# 按键排序
sorted_by_name = dict(sorted(scores.items()))
print(f"按姓名排序: {sorted_by_name}")
# 多层排序
students = [
{"name": "张三", "class": "A", "score": 85},
{"name": "李四", "class": "B", "score": 92},
{"name": "王五", "class": "A", "score": 88},
{"name": "赵六", "class": "B", "score": 78}
]
# 先按班级,再按分数排序
sorted_students = sorted(students, key=lambda x: (x["class"], -x["score"]))
print("多层排序结果:")
for student in sorted_students:
print(f" {student['class']}班 {student['name']}: {student['score']}分")
集合的高级用法
1. 集合推导式
# 基本集合推导式
even_squares = {x**2 for x in range(10) if x % 2 == 0}
print(f"偶数平方: {even_squares}")
# 从字符串创建唯一字符
unique_chars = {char for char in "hello world" if char.isalpha()}
print(f"唯一字符: {unique_chars}")
# 复杂的集合推导式
import math
prime_candidates = {x for x in range(2, 20)
if all(x % i != 0 for i in range(2, int(math.sqrt(x)) + 1))}
print(f"质数候选: {prime_candidates}")
2. 冻结集合(frozenset)
# 创建 frozenset
frozen = frozenset([1, 2, 3, 2, 1])
print(f"冻结集合: {frozen}")
# frozenset 是不可变的,可以作为字典的键
course_prerequisites = {
frozenset(["数学", "物理"]): "高等数学",
frozenset(["编程", "算法"]): "数据结构",
frozenset(["英语"]): "基础英语"
}
# 查找课程
math_physics = frozenset(["数学", "物理"])
print(f"数学+物理的先修课程: {course_prerequisites[math_physics]}")
# frozenset 支持集合运算
set1 = frozenset([1, 2, 3])
set2 = frozenset([3, 4, 5])
union = set1 | set2
intersection = set1 & set2
print(f"并集: {union}")
print(f"交集: {intersection}")
3. 集合的实际应用
# 1. 标签系统
class TagSystem:
def __init__(self):
self.items = {} # 物品到标签的映射
self.tags = {} # 标签到物品的映射
def add_item(self, item, tags):
"""添加物品和标签"""
self.items[item] = set(tags)
for tag in tags:
if tag not in self.tags:
self.tags[tag] = set()
self.tags[tag].add(item)
def find_items_by_tags(self, required_tags, optional_tags=None):
"""根据标签查找物品"""
if not required_tags:
return set()
required_set = set(required_tags)
# 找到包含所有必需标签的物品
result = None
for tag in required_set:
if tag in self.tags:
if result is None:
result = self.tags[tag].copy()
else:
result &= self.tags[tag]
else:
return set() # 有必需标签不存在
# 如果有可选标签,按匹配数量排序
if optional_tags and result:
optional_set = set(optional_tags)
scored_items = []
for item in result:
item_tags = self.items[item]
optional_matches = len(item_tags & optional_set)
scored_items.append((optional_matches, item))
# 按匹配数量降序排序
scored_items.sort(reverse=True)
return [item for _, item in scored_items]
return result if result else set()
def get_similar_items(self, item, limit=5):
"""找到相似物品(基于标签重叠)"""
if item not in self.items:
return []
item_tags = self.items[item]
similarities = []
for other_item, other_tags in self.items.items():
if other_item != item:
common_tags = len(item_tags & other_tags)
total_tags = len(item_tags | other_tags)
similarity = common_tags / total_tags if total_tags > 0 else 0
similarities.append((similarity, other_item))
# 按相似度降序排序
similarities.sort(reverse=True)
return [item for _, item in similarities[:limit]]
# 测试标签系统
tag_system = TagSystem()
# 添加一些物品
tag_system.add_item("Python编程书", ["编程", "Python", "入门", "书籍"])
tag_system.add_item("数据科学课程", ["编程", "Python", "数据科学", "在线课程"])
tag_system.add_item("JavaScript教程", ["编程", "JavaScript", "前端", "入门"])
tag_system.add_item("机器学习书籍", ["编程", "Python", "机器学习", "书籍", "进阶"])
tag_system.add_item("摄影入门", ["摄影", "入门", "艺术"])
# 搜索物品
print("搜索包含'编程'和'Python'的物品:")
results = tag_system.find_items_by_tags(["编程", "Python"])
for item in results:
print(f" {item}")
print("\n搜索包含'编程',最好还有'入门'的物品:")
results = tag_system.find_items_by_tags(["编程"], ["入门"])
for item in results:
print(f" {item}")
print("\n与'Python编程书'相似的物品:")
similar = tag_system.get_similar_items("Python编程书")
for item in similar:
print(f" {item}")
性能分析和优化
1. 字典性能测试
import time
import random
import string
def performance_test():
"""字典性能测试"""
# 生成测试数据
def generate_random_string(length=10):
return ''.join(random.choices(string.ascii_lowercase, k=length))
# 测试不同大小的字典
sizes = [100, 1000, 10000]
for size in sizes:
print(f"\n测试字典大小: {size}")
# 创建字典
test_dict = {generate_random_string(): i for i in range(size)}
test_keys = list(test_dict.keys())
# 测试查找性能
start_time = time.time()
for _ in range(1000):
key = random.choice(test_keys)
value = test_dict[key]
lookup_time = time.time() - start_time
# 测试插入性能
start_time = time.time()
for i in range(1000):
test_dict[generate_random_string()] = i + size
insert_time = time.time() - start_time
# 测试删除性能
delete_keys = [generate_random_string() for _ in range(1000)]
for key in delete_keys:
test_dict[key] = 0 # 先插入
start_time = time.time()
for key in delete_keys:
if key in test_dict:
del test_dict[key]
delete_time = time.time() - start_time
print(f"查找1000次: {lookup_time:.4f}秒")
print(f"插入1000次: {insert_time:.4f}秒")
print(f"删除1000次: {delete_time:.4f}秒")
performance_test()
2. 内存使用优化
import sys
from collections import OrderedDict, defaultdict
def memory_comparison():
"""内存使用比较"""
# 测试数据
data = {f"key_{i}": f"value_{i}" for i in range(1000)}
# 普通字典
regular_dict = data.copy()
# OrderedDict(Python 3.7+ 之前保持插入顺序)
ordered_dict = OrderedDict(data)
# defaultdict
default_dict = defaultdict(str, data)
# 计算内存使用
regular_size = sys.getsizeof(regular_dict)
for key, value in regular_dict.items():
regular_size += sys.getsizeof(key) + sys.getsizeof(value)
ordered_size = sys.getsizeof(ordered_dict)
for key, value in ordered_dict.items():
ordered_size += sys.getsizeof(key) + sys.getsizeof(value)
default_size = sys.getsizeof(default_dict)
for key, value in default_dict.items():
default_size += sys.getsizeof(key) + sys.getsizeof(value)
print(f"普通字典内存: {regular_size} 字节")
print(f"OrderedDict内存: {ordered_size} 字节")
print(f"defaultdict内存: {default_size} 字节")
memory_comparison()
常见陷阱和最佳实践
1. 字典键的可变性
# ⚠️ 错误:使用可变对象作为字典键
try:
bad_dict = {[1, 2]: "value"}
except TypeError as e:
print(f"错误: {e}")
# ✅ 正确:使用不可变对象作为键
# 使用元组代替列表
good_dict = {(1, 2): "value"}
print(f"元组作为键: {good_dict}")
# 使用字符串
string_dict = {"key_1_2": "value"}
print(f"字符串作为键: {string_dict}")
# 使用 frozenset
frozen_dict = {frozenset([1, 2]): "value"}
print(f"frozenset作为键: {frozen_dict}")
2. 集合的修改陷阱
# ⚠️ 错误:在遍历集合时修改它
numbers = {1, 2, 3, 4, 5}
try:
for num in numbers:
if num % 2 == 0:
numbers.remove(num) # 运行时错误
except RuntimeError as e:
print(f"错误: {e}")
# ✅ 正确:先创建要删除的列表
to_remove = [num for num in numbers if num % 2 == 0]
for num in to_remove:
numbers.remove(num)
print(f"删除偶数后: {numbers}")
# 或者使用集合推导式创建新集合
numbers = {1, 2, 3, 4, 5}
filtered = {num for num in numbers if num % 2 != 0}
print(f"过滤后: {filtered}")
3. 性能陷阱
# ⚠️ 低效的键存在检查
large_dict = {f"key_{i}": i for i in range(10000)}
# 低效的方法:遍历所有键
def slow_lookup(dictionary, key):
for k in dictionary:
if k == key:
return dictionary[k]
return None
# 高效的方法:直接使用键访问
def fast_lookup(dictionary, key):
return dictionary.get(key)
import time
# 性能比较
key = "key_5000"
start = time.time()
for _ in range(1000):
slow_result = slow_lookup(large_dict, key)
slow_time = time.time() - start
start = time.time()
for _ in range(1000):
fast_result = fast_lookup(large_dict, key)
fast_time = time.time() - start
print(f"慢速查找: {slow_time:.4f}秒")
print(f"快速查找: {fast_time:.4f}秒")
print(f"加速比: {slow_time/fast_time:.0f}倍")
总结
字典和集合是 Python 中基于哈希表实现的强大数据结构:
- 字典提供了键值对的快速存储和检索,平均时间复杂度为 O(1)
- 集合提供了唯一元素的集合运算,支持并集、交集、差集等操作
- 哈希机制是这些数据结构高效性的基础,理解哈希冲突有助于编写更好的代码
- 性能优化方面,字典和集合的查找、插入、删除操作都非常高效
- 使用场景:字典适合存储结构化数据,集合适合处理唯一性和集合运算
掌握字典和集合的使用,能够让你编写出更加高效和优雅的 Python 代码。记住:
- 使用不可变对象作为字典键
- 利用集合进行唯一性检查和集合运算
- 理解哈希原理有助于避免性能陷阱
- 选择合适的数据结构对程序性能至关重要
在下一篇文章中,我们将探讨 Python 的控制流,学习如何使用条件语句、循环和各种控制结构来构建复杂的程序逻辑。
结尾
希望对初学者有帮助;致力于办公自动化的小小程序员一枚
希望能得到大家的【❤️一个免费关注❤️】感谢!
求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍
此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏
此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏
此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏
1753

被折叠的 条评论
为什么被折叠?



