Python字典与集合

学习目标

通过本集的学习,你将能够:

  • 理解字典的键值对概念
  • 掌握字典的基本操作
  • 理解集合的特性
  • 使用集合进行数学运算
  • 选择合适的数据结构

1. 字典的键值对

字典是Python中另一种重要的数据结构,它存储键值对(key-value pairs)。

1.1 创建字典

# 空字典
empty_dict = {}
print(empty_dict)  # 输出: {}

# 使用大括号创建
person = {
    "name": "张三",
    "age": 25,
    "city": "北京"
}
print(person)

# 使用dict()创建
person2 = dict(name="李四", age=30, city="上海")
print(person2)

# 键可以是不可变类型(字符串、数字、元组等)
# 值可以是任意类型
mixed = {
    1: "one",
    "two": 2,
    (3, 4): "tuple key",
    "nested": {"a": 1, "b": 2}
}
print(mixed)

字典的ASCII图表示:

字典: {"name": "张三", "age": 25, "city": "北京"}

    ┌─────────────┐
    │   "name"    │───→ "张三"
    ├─────────────┤
    │   "age"     │───→ 25
    ├─────────────┤
    │   "city"    │───→ "北京"
    └─────────────┘
       键 (Key)        值 (Value)

1.2 访问字典元素

person = {"name": "张三", "age": 25, "city": "北京"}

# 使用 [] 访问
print(person["name"])   # 输出: 张三
print(person["age"])    # 输出: 25

# 使用 get() 访问(更安全)
print(person.get("name"))        # 输出: 张三
print(person.get("gender"))      # 输出: None(键不存在时返回None)
print(person.get("gender", "未知"))  # 输出: 未知(指定默认值)

# 检查键是否存在
print("name" in person)   # 输出: True
print("gender" in person) # 输出: False

2. 字典的基本操作

2.1 添加和修改元素

person = {"name": "张三", "age": 25}

# 添加新键值对
person["city"] = "北京"
print(person)  # 输出: {'name': '张三', 'age': 25, 'city': '北京'}

# 修改已有键的值
person["age"] = 26
print(person)  # 输出: {'name': '张三', 'age': 26, 'city': '北京'}

# 使用 update() 批量更新
person.update({"age": 27, "gender": "男", "job": "工程师"})
print(person)

2.2 删除元素

person = {"name": "张三", "age": 25, "city": "北京", "job": "工程师"}

# 使用 del 删除
del person["job"]
print(person)

# 使用 pop() 删除并返回值
age = person.pop("age")
print(age)      # 输出: 25
print(person)   # 输出: {'name': '张三', 'city': '北京'}

# pop() 可以指定默认值
gender = person.pop("gender", "未知")
print(gender)   # 输出: 未知

# 使用 popitem() 删除最后一个键值对(Python 3.7+)
last = person.popitem()
print(last)     # 输出: ('city', '北京')
print(person)   # 输出: {'name': '张三'}

# 使用 clear() 清空字典
person.clear()
print(person)   # 输出: {}

2.3 字典方法

person = {"name": "张三", "age": 25, "city": "北京"}

# 获取所有键
keys = person.keys()
print(keys)          # 输出: dict_keys(['name', 'age', 'city'])
print(list(keys))    # 输出: ['name', 'age', 'city']

# 获取所有值
values = person.values()
print(values)        # 输出: dict_values(['张三', 25, '北京'])
print(list(values))  # 输出: ['张三', 25, '北京']

# 获取所有键值对
items = person.items()
print(items)         # 输出: dict_items([('name', '张三'), ('age', 25), ('city', '北京')])
print(list(items))   # 输出: [('name', '张三'), ('age', 25), ('city', '北京')]

# 复制字典
person2 = person.copy()
print(person2)

# 长度
print(len(person))   # 输出: 3

2.4 遍历字典

person = {"name": "张三", "age": 25, "city": "北京"}

# 遍历键
for key in person:
    print(key)

# 遍历键(明确方式)
for key in person.keys():
    print(key)

# 遍历值
for value in person.values():
    print(value)

# 遍历键值对
for key, value in person.items():
    print(f"{key}: {value}")

2.5 字典推导式

# 基本字典推导式
squares = {x: x * x for x in range(5)}
print(squares)  # 输出: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 带条件的字典推导式
even_squares = {x: x * x for x in range(10) if x % 2 == 0}
print(even_squares)  # 输出: {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

# 从两个列表创建字典
keys = ["name", "age", "city"]
values = ["张三", 25, "北京"]
person = {k: v for k, v in zip(keys, values)}
print(person)

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

3. 集合的特性

集合是无序、不重复的元素集合。

3.1 创建集合

# 空集合(注意:{}是空字典,不是空集合)
empty_set = set()
print(empty_set)  # 输出: set()

# 使用大括号创建
fruits = {"apple", "banana", "cherry"}
print(fruits)

# 集合自动去重
numbers = {1, 2, 2, 3, 3, 3, 4}
print(numbers)  # 输出: {1, 2, 3, 4}

# 使用set()创建
letters = set("hello")
print(letters)  # 输出: {'h', 'e', 'l', 'o'}

list_to_set = set([1, 2, 3, 2, 1])
print(list_to_set)  # 输出: {1, 2, 3}

集合的ASCII图表示:

集合: {1, 2, 3, 4}

  ┌───┐ ┌───┐ ┌───┐ ┌───┐
  │ 1 │ │ 2 │ │ 3 │ │ 4 │
  └───┘ └───┘ └───┘ └───┘
  
  特点:
  - 无序:没有固定顺序
  - 不重复:每个元素唯一
  - 可变:可以添加/删除元素

3.2 集合操作

fruits = {"apple", "banana", "cherry"}

# 添加元素
fruits.add("date")
print(fruits)  # 输出: {'apple', 'banana', 'cherry', 'date'}

# 添加多个元素
fruits.update(["elderberry", "fig"])
print(fruits)

# 删除元素
fruits.remove("banana")
print(fruits)

# discard():删除不存在的元素不会报错
fruits.discard("banana")  # 不会报错

# pop():随机删除一个元素并返回
popped = fruits.pop()
print(popped)
print(fruits)

# 清空集合
fruits.clear()
print(fruits)  # 输出: set()

3.3 集合运算

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

# 并集:所有在A或B中的元素
union = A | B
print(union)  # 输出: {1, 2, 3, 4, 5, 6}
print(A.union(B))

# 交集:同时在A和B中的元素
intersection = A & B
print(intersection)  # 输出: {3, 4}
print(A.intersection(B))

# 差集:在A中但不在B中的元素
difference = A - B
print(difference)  # 输出: {1, 2}
print(A.difference(B))

# 对称差集:在A或B中,但不同时在两者中
symmetric_diff = A ^ B
print(symmetric_diff)  # 输出: {1, 2, 5, 6}
print(A.symmetric_difference(B))

集合运算的ASCII图:

并集 A | B:
  ┌─────────┐
  │ A   ●   │
  │   ● ●   │
  │ ●   ● B │
  └─────────┘

交集 A & B:
  ┌─────────┐
  │ A       │
  │   ●●    │
  │       B │
  └─────────┘

差集 A - B:
  ┌─────────┐
  │ A ●●    │
  │         │
  │       B │
  └─────────┘

3.4 集合关系

A = {1, 2, 3}
B = {1, 2, 3, 4, 5}
C = {4, 5, 6}

# 子集:A的所有元素都在B中
print(A.issubset(B))   # 输出: True
print(A <= B)           # 输出: True

# 超集:B包含A的所有元素
print(B.issuperset(A)) # 输出: True
print(B >= A)           # 输出: True

# 真子集
print(A < B)            # 输出: True
print(A < A)            # 输出: False

# 不相交:没有共同元素
print(A.isdisjoint(C))  # 输出: True
print(A.isdisjoint(B))  # 输出: False

4. frozenset:不可变集合

# 创建frozenset
fs = frozenset([1, 2, 3, 4])
print(fs)  # 输出: frozenset({1, 2, 3, 4})

# frozenset可以作为字典的键
d = {fs: "这是一个frozenset"}
print(d)

# frozenset支持集合运算
fs2 = frozenset([3, 4, 5, 6])
print(fs & fs2)  # 输出: frozenset({3, 4})

# 但不能修改
# fs.add(5)  # 错误:AttributeError

5. 实用案例

5.1 案例1:单词频率统计

# word_count.py

def count_words(text):
    """统计单词出现频率"""
    # 转换为小写并分割
    words = text.lower().split()
    # 去除标点
    words = [word.strip(",.!?;:") for word in words]
    
    # 统计频率
    word_count = {}
    for word in words:
        if word:
            word_count[word] = word_count.get(word, 0) + 1
    
    return word_count

def print_word_count(word_count):
    """打印单词频率"""
    # 按频率排序
    sorted_words = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
    
    for word, count in sorted_words:
        print(f"{word}: {count}")

# 测试
sample_text = """Python is a great language. Python is easy to learn.
I love Python! Python is fun."""

word_count = count_words(sample_text)
print("单词频率统计:")
print_word_count(word_count)

# 使用集合找出唯一单词
unique_words = set(word_count.keys())
print(f"\n唯一单词数: {len(unique_words)}")
print("唯一单词:", unique_words)

5.2 案例2:通讯录管理

# contact_book.py

class ContactBook:
    def __init__(self):
        self.contacts = {}
    
    def add(self, name, phone, email=None):
        """添加联系人"""
        self.contacts[name] = {
            "phone": phone,
            "email": email
        }
        print(f"已添加联系人: {name}")
    
    def search(self, name):
        """查找联系人"""
        if name in self.contacts:
            info = self.contacts[name]
            print(f"{name}:")
            print(f"  电话: {info['phone']}")
            if info['email']:
                print(f"  邮箱: {info['email']}")
        else:
            print(f"未找到联系人: {name}")
    
    def delete(self, name):
        """删除联系人"""
        if name in self.contacts:
            del self.contacts[name]
            print(f"已删除联系人: {name}")
        else:
            print(f"未找到联系人: {name}")
    
    def list_all(self):
        """列出所有联系人"""
        if not self.contacts:
            print("通讯录为空")
            return
        
        print("所有联系人:")
        for name in sorted(self.contacts.keys()):
            print(f"  - {name}")

# 使用
book = ContactBook()
book.add("张三", "13800138000", "zhangsan@example.com")
book.add("李四", "13900139000")
book.add("王五", "13700137000", "wangwu@example.com")

print()
book.list_all()

print()
book.search("张三")

print()
book.delete("李四")

print()
book.list_all()

5.3 案例3:集合在数据分析中的应用

# set_analysis.py

# 学生选课数据
math_students = {"张三", "李四", "王五", "赵六"}
physics_students = {"李四", "王五", "钱七", "孙八"}
chemistry_students = {"王五", "赵六", "孙八", "周九"}

print("数学选课学生:", math_students)
print("物理选课学生:", physics_students)
print("化学选课学生:", chemistry_students)

# 同时选了数学和物理的学生
both_math_physics = math_students & physics_students
print(f"\n同时选数学和物理: {both_math_physics}")

# 选了数学但没选物理的学生
only_math = math_students - physics_students
print(f"只选数学: {only_math}")

# 所有选课的学生
all_students = math_students | physics_students | chemistry_students
print(f"所有选课学生: {all_students}")
print(f"总人数: {len(all_students)}")

# 选了所有三门课的学生
all_three = math_students & physics_students & chemistry_students
print(f"选了所有三门课: {all_three}")

# 只选了一门课的学生
only_one = (math_students ^ physics_students ^ chemistry_students) - all_three
print(f"只选一门课: {only_one}")

6. 自测问题

  1. 字典的键和值有什么要求?
  2. 如何安全地访问字典中可能不存在的键?
  3. 集合的主要特点是什么?
  4. 集合的并集、交集、差集如何表示?
  5. 什么时候使用字典,什么时候使用集合?

7. 下集预告

下一集我们将学习Python的类与对象入门!

参考资料

« 上一篇 Python列表与元组 下一篇 » Python类与对象入门