Python数据结构-字典与集合


在 Python 中,字典(dict)集合(set) 是两种基于哈希表的无序数据结构,它们的核心特点是查找效率极高(平均时间复杂度为 O(1)),但用法和场景不同:字典用于存储键值对映射,集合用于存储不重复的元素

一、字典(dict):键值对的集合

字典是 Python 中最灵活的映射类型,用花括号 {} 表示,元素以 键: 值 的形式存储,键(key)唯一且不可变,值(value)可以是任意类型(包括列表、字典等)。

核心特性:键唯一、键不可变、无序(Python 3.7+ 开始保证插入顺序)、查询速度快。

1. 字典的创建

# 直接用 {} 创建(键值对用逗号分隔)
empty_dict = {}  # 空字典
user = {
    "name": "小明",
    "age": 18,
    "is_student": True
}  # 包含多个键值对的字典

# 用 dict() 函数创建(通过键值对参数或可迭代对象)
dict1 = dict(name="小红", age=20)  # 关键字参数 → {'name': '小红', 'age': 20}
dict2 = dict([("gender", "女"), ("score", 90)])  # 列表转字典 → {'gender': '女', 'score': 90}

# 嵌套字典(值可以是字典)
school = {
    "name": "阳光中学",
    "classes": {"class1": 45, "class2": 50}  # 嵌套字典
}

2. 字典的核心操作

(1)访问值(通过键)
  • 直接用 dict[key]:若键不存在,会抛出 KeyError
  • dict.get(key, default):若键不存在,返回 default(默认 None,更安全)。
user = {"name": "小明", "age": 18}
print(user["name"])  # 通过键访问 → "小明"
print(user.get("gender", "未知"))  # 键不存在,返回默认值 → "未知"
(2)添加/修改键值对
  • 若键已存在,赋值会修改对应的值;
  • 若键不存在,赋值会新增键值对。
user = {"name": "小明", "age": 18}
user["age"] = 19  # 修改已有键 → {'name': '小明', 'age': 19}
user["gender"] = "男"  # 新增键 → {'name': '小明', 'age': 19, 'gender': '男'}
(3)删除键值对
  • del dict[key]:删除指定键(键不存在则报错)。
  • dict.pop(key, default):删除指定键并返回对应值(键不存在则返回 default)。
  • dict.clear():清空字典(变为空字典)。
user = {"name": "小明", "age": 18}
del user["age"]  # 删除键"age" → {'name': '小明'}

score = user.pop("score", 0)  # 键"score"不存在,返回0 → user仍为{'name': '小明'}

user.clear()  # 清空 → {}
(4)字典的遍历

常用方法:遍历键、遍历值、遍历键值对。

user = {"name": "小明", "age": 18, "gender": "男"}

# 遍历键(默认遍历键)
for key in user:
    print(key)  # 输出:name、age、gender

# 遍历值(用 .values())
for value in user.values():
    print(value)  # 输出:小明、18、男

# 遍历键值对(用 .items(),返回 (key, value) 元组)
for key, value in user.items():
    print(f"{key}: {value}")  # 输出:name: 小明、age: 18、gender: 男
(5)其他常用方法
  • dict.keys():返回所有键的视图(类似列表,可迭代)。
  • dict.values():返回所有值的视图。
  • dict.items():返回所有键值对的视图((key, value) 元组)。
  • dict.update(other_dict):用 other_dict 的键值对更新当前字典(键存在则覆盖,不存在则新增)。
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
dict1.update(dict2)  # 用dict2更新dict1 → {'a': 1, 'b': 3, 'c': 4}

二、集合(set):不重复元素的集合

集合是无序的、元素唯一的集合,用花括号 {} 表示(元素直接用逗号分隔),或通过 set() 函数创建。

核心特性:元素唯一(自动去重)、元素不可变(如字符串、数字、元组)、无序、支持集合运算(交集、并集等)。

1. 集合的创建

# 直接用 {} 创建(注意:空集合不能用 {},{} 表示空字典)
num_set = {1, 2, 3, 4}  # 包含多个元素的集合
str_set = {"a", "b", "c"}

# 用 set() 函数创建(转换可迭代对象,如列表、字符串)
list_to_set = set([1, 2, 2, 3])  # 列表转集合(自动去重)→ {1, 2, 3}
str_to_set = set("hello")  # 字符串转集合(去重)→ {'h', 'e', 'l', 'o'}(无序)

empty_set = set()  # 空集合(必须用 set())

2. 集合的核心操作

(1)添加元素
  • set.add(x):添加单个元素 x(若 x 已存在,不做任何操作)。
  • set.update(iterable):添加可迭代对象(如列表、集合)中的所有元素(自动去重)。
s = {1, 2, 3}
s.add(4)  # 添加元素 → {1, 2, 3, 4}
s.add(2)  # 元素已存在,无变化 → {1, 2, 3, 4}

s.update([4, 5, 6])  # 添加列表元素 → {1, 2, 3, 4, 5, 6}
(2)删除元素
  • set.remove(x):删除元素 x(若 x 不存在,抛出 KeyError)。
  • set.discard(x):删除元素 x(若 x 不存在,不报错)。
  • set.pop():随机删除并返回一个元素(因集合无序,无法指定)。
  • set.clear():清空集合(变为空集合)。
s = {1, 2, 3, 4}
s.remove(3)  # 删除3 → {1, 2, 4}
s.discard(5)  # 5不存在,无变化 → {1, 2, 4}

removed = s.pop()  # 随机删除一个元素(如1)→ s变为 {2, 4},removed=1

s.clear()  # 清空 → set()
(3)集合运算(类似数学中的集合)

假设有两个集合 a = {1, 2, 3, 4}b = {3, 4, 5, 6}

运算 符号 方法 描述 结果示例
交集 & a.intersection(b) 两个集合的共同元素 {3, 4}
并集 | a.union(b) 两个集合的所有元素(去重) {1, 2, 3, 4, 5, 6}
差集 - a.difference(b) 属于a但不属于b的元素 {1, 2}
对称差集 ^ a.symmetric_difference(b) 仅属于a或仅属于b的元素 {1, 2, 5, 6}

示例

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a & b)  # 交集 → {3, 4}
print(a | b)  # 并集 → {1, 2, 3, 4, 5, 6}
print(a - b)  # 差集 → {1, 2}
(4)成员关系检查(in / not in
s = {1, 2, 3}
print(2 in s)      # → True
print(4 not in s)  # → True

三、字典与集合的对比

特性 字典(dict) 集合(set)
存储形式 键值对(key: value 单一元素(无键)
核心作用 映射关系(通过键查值) 去重、集合运算、成员检查
元素要求 键不可变且唯一,值任意 元素不可变且唯一
空对象创建 {}dict() 只能用 set(){} 是字典)
无序性 Python 3.7+ 保证插入顺序 始终无序(无索引)

四、使用场景建议

  • 用字典:当需要存储“键-值”映射关系时(如用户信息、配置参数、缓存数据),例如 {"id": 1001, "name": "张三"}
  • 用集合:当需要去重(如列表去重 list(set(lst)))、检查元素是否存在(比列表更高效)、或进行集合运算(如求共同好友、差集分析)时。

总结:字典和集合均基于哈希表,查找效率极高,但字典聚焦于“键值映射”,集合聚焦于“唯一元素与集合运算”。根据数据的存储需求选择合适的类型,能显著提升代码效率。

总结

相比于列表和元组,字典的性能更优,特别是对于查找、添加和删除操作,字典都能在常数时间复杂度内完成。

而集合和字典基本相同,唯一的区别,就是集合没有键和值的配对,是一系列无序的、唯一的元素组合。

1.字典和集合的创建

>>>d1 = {'name': 'jason', 'age': 20, 'gender': 'male'}
>>>d2 = dict({'name': 'jason', 'age': 20, 'gender': 'male'})
>>>d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')])
>>>d4 = dict(name='jason', age=20, gender='male')
>>>d1 == d2 == d3 == d4
True
>>>s1 = {1, 2, 3}
>>>s2 = set([1, 2, 3])
>>>s1 == s2
True

2.字典和集合的特性

1)Python 中字典和集合

  • key的类型必须为不可变类型:字符串、数字、元组。

  • value的类型为任意类型。

2)字典的元素访问

字典访问可以直接索引键,如果不存在,就会抛出异常:

>>>d ={'name': 'jason', 'age': 20}
>>>d['name']
'jason'
>>>d['location']
Traceback (most recent call last): File"<stdin>", line 1, in<module>KeyError: 'location'

也可以使用 get(key, default) 函数来进行索引。如果键不存在,调用 get() 函数可以返回一个默认值。比如下面这个示例,返回了'null'

>>>d ={'name': 'jason', 'age': 20}
>>>d.get('name')
'jason'
>>>d.get('location', 'null')#可以自行设置默认值'null'

3) 集合的访问

集合并不支持索引操作,因为集合本质上是一个哈希表,和列表不一样。所以,下面这样的操作是错误的,Python 会抛出异常:

>>>s ={1, 2, 3}
>>>s[0]
Traceback (most recent call last): File"<stdin>", line 1, in<module>TypeError: 'set'objectdoes notsupport indexing

4)判断一个元素在不在字典或集合内,我们可以用 value in dict/set 来判断

>>>s ={1, 2, 3}
>>>1 in s
True
>>>10 in s
False
>>>d ={'name': 'jason', 'age': 20}
>>>'name' in d
True
>>>'location' in d
False

5) 字典和集合也同样支持增加、删除、更新等操作

集合的 pop() 操作是删除集合中最后一个元素,可是集合本身是无序的,无法知道会删除哪个元素,因此这个操作得谨慎使用!❗️❗️

6) 对字典或集合进行排序

对字典的排序:

>>>d ={'b': 1, 'a': 2, 'c': 10}
>>>d_sorted_by_key =sorted(d.items(), key=lambdax: x[0]) # 根据字典键的升序排序
>>>d_sorted_by_value =sorted(d.items(), key=lambdax: x[1]) # 根据字典值的升序排序
>>>d_sorted_by_key
[('a', 2), ('b', 1), ('c',10)]
>>>d_sorted_by_value
[('b', 1), ('a', 2), ('c', 10)]

对集合排序

#直接调用 sorted(set) 即可,结果会返回一个排好序的列表
>>>s ={3, 4, 2, 1}
>>>sorted(s) # 对集合的元素进行升序排序
[1, 2, 3, 4]

7) 字典和集合性能

字典和集合是进行过性能高度优化的数据结构,特别是对于查找、添加和删除操作

8)集合运算

集合是由不重复元素组成的无序容器。基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算。

创建集合用花括号或 set() 函数。注意,创建空集合只能用 set(),不能用 {}{} 创建的是空字典,下一小节介绍数据结构:字典。

以下是一些简单的示例

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket                 # fast membership testing
True
>>> 'crabgrass' in basket
False

>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # letters in a but not in b
{'r', 'd', 'b'}
>>> a | b                              # letters in a or b or both
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # letters in both a and b
{'a', 'c'}
>>> a ^ b                              # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}

列表推导式 类似,集合也支持推导式:

>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}

字典推导式可以用任意键值表达式创建字典:

>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

关键字是比较简单的字符串时,直接用关键字参数指定键值对更便捷:

>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

9) 循环的技巧

在字典中循环时,用 items() 方法可同时取出键和对应的值:

>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

在序列中循环时,用 enumerate() 函数可以同时取出位置索引和对应的值:

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

同时循环两个或多个序列时,用 zip() 函数可以将其内的元素一一匹配:

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

逆向循环序列时,先正向定位序列,然后调用 reversed() 函数:

>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

按指定顺序循环序列,可以用 sorted() 函数,在不改动原序列的基础上,返回一个重新的序列:

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for i in sorted(basket):
...     print(i)
...
apple
apple
banana
orange
orange
pear

使用 set() 去除序列中的重复元素。使用 sorted()set() 则按排序后的顺序,循环遍历序列中的唯一元素:

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

一般来说,在循环中修改列表的内容时,创建新列表比较简单,且安全:

>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
...     if not math.isnan(value):
...         filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]

10) 深入条件控制

whileif 条件句不只可以进行比较,还可以使用任意运算符。

比较运算符 innot in 校验序列里是否存在某个值。运算符 isis not 比较两个对象是否为同一个对象。所有比较运算符的优先级都一样,且低于数值运算符。

比较操作支持链式操作。例如,a < b == c 校验 a 是否小于 b,且 b 是否等于 c

比较操作可以用布尔运算符 andor 组合,并且,比较操作(或其他布尔运算)的结果都可以用 not 取反。这些操作符的优先级低于比较操作符;not 的优先级最高, or 的优先级最低,因此,A and not B or C 等价于 (A and (not B)) or C。与其他运算符操作一样,此处也可以用圆括号表示想要的组合。

布尔运算符 andor 也称为 短路 运算符:其参数从左至右解析,一旦可以确定结果,解析就会停止。例如,如果 AC 为真,B 为假,那么 A and B and C 不会解析 C。用作普通值而不是布尔值时,短路操作符返回的值通常是最后一个变量。

还可以把比较操作或逻辑表达式的结果赋值给变量,例如:

>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

注意,Python 与 C 不同,在表达式内部赋值必须显式使用 海象运算符 :=。 这避免了 C 程序中常见的问题:要在表达式中写 == 时,却写成了 =

11) 比较大小

序列对象可以与相同序列类型的其他对象比较。这种比较使用 字典式 顺序:首先,比较前两个对应元素,如果不相等,则可确定比较结果;如果相等,则比较之后的两个元素,以此类推,直到其中一个序列结束。如果要比较的两个元素本身是相同类型的序列,则递归地执行字典式顺序比较。如果两个序列中所有的对应元素都相等,则两个序列相等。如果一个序列是另一个的初始子序列,则较短的序列可被视为较小(较少)的序列。 对于字符串来说,字典式顺序使用 Unicode 码位序号排序单个字符。下面列出了一些比较相同类型序列的例子:

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

注意,对不同类型的对象来说,只要待比较的对象提供了合适的比较方法,就可以使用 <> 进行比较。例如,混合数值类型通过数值进行比较,所以,0 等于 0.0,等等。否则,解释器不会随便给出一个对比结果,而是触发 TypeError 异常。