Python字典dict特点与集合set有序性说明
一、dict(字典)的特点
1. 键值对存储
字典存的是 key-value(键值对),通过键来查找值。
person = {"name": "小明", "age": 25, "city": "北京"}
print(person["name"]) # 小明
2. 键必须唯一,值可以重复
d = {"a": 1, "b": 2, "a": 3} # 键 a 重复,后面的覆盖前面的
print(d) # {'a': 3, 'b': 2}
值可以重复:
d = {"a": 1, "b": 1, "c": 1} # 合法,值都是 1
3. 键必须可哈希(不可变),值任意
可以作为键的:字符串、数字、元组、布尔值、None。
不能作为键的:列表、字典、集合(因为它们是可变的)。
d = {}
d["name"] = "小明" # ✅ 字符串作键
d[1] = "one" # ✅ 数字作键
d[(1, 2)] = "元组作键" # ✅ 元组作键
d[[1, 2]] = "列表作键" # ❌ TypeError: unhashable type: 'list'
4. 查找效率 O(1)
底层是哈希表,按键查找的平均时间复杂度是 O(1)。
d = {"a": 1, "b": 2}
print(d["a"]) # 瞬间找到,不管字典多大
5. 有序(Python 3.7+)
从 Python 3.7 开始,字典保证按插入顺序遍历。
d = {"b": 2, "a": 1, "c": 3}
print(list(d.keys())) # ['b', 'a', 'c'],按插入顺序
在 Python 3.6 及更早版本,字典是无序的。如果面试被问到,要说清楚版本。
6. 可变
可以随时增删改:
d = {"a": 1}
d["b"] = 2 # 增
d["a"] = 10 # 改
del d["b"] # 删
7. 常用操作
d = {"name": "小明", "age": 25}
# 查
d["name"] # 小明,键不存在会 KeyError
d.get("gender") # None,键不存在返回 None
d.get("gender", "未知") # "未知",指定默认值
# 增/改
d["city"] = "北京" # 键不存在则增,存在则改
# 删
del d["age"] # 删除键,不存在会 KeyError
d.pop("age", None) # 安全删除,不存在返回默认值
d.clear() # 清空
# 遍历
for key in d: # 遍历键
...
for key, value in d.items(): # 遍历键值对
...
for value in d.values(): # 遍历值
...
# 成员判断(判断键)
if "name" in d:
...
8. 字典推导式
squares = {x: x**2 for x in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
二、set(集合)的特点
1. 无序、不重复
集合是一组唯一的、无序的元素。
s = {1, 2, 3, 2, 1}
print(s) # {1, 2, 3},自动去重
2. 元素必须可哈希
和字典的键一样,集合元素必须是不可变类型。
s = {1, 2, "hello", (1, 2)} # ✅
s = {[1, 2]} # ❌ TypeError: unhashable type: 'list'
3. 无序
集合不保证顺序,遍历顺序是不确定的。
s = {3, 1, 2}
print(s) # 可能是 {1, 2, 3},也可能是 {3, 1, 2},取决于哈希值
注意:集合不是“有序”的。虽然打印出来看起来有顺序,但那是哈希表的内部排列,不是你插入的顺序。不同版本、不同运行环境可能不一样。
4. 查找效率 O(1)
底层也是哈希表,判断元素是否存在非常快。
s = {1, 2, 3, 4, 5}
print(3 in s) # True,O(1)
5. 可变(但元素不可变)
集合本身可以增删元素:
s = {1, 2, 3}
s.add(4) # 增
s.remove(1) # 删,不存在会 KeyError
s.discard(10) # 删,不存在不报错
s.pop() # 随机删一个
s.clear() # 清空
但元素本身不能是可变类型。
6. 常用操作
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
# 并集
a | b # {1, 2, 3, 4, 5, 6}
a.union(b) # 同上
# 交集
a & b # {3, 4}
a.intersection(b) # 同上
# 差集(在 a 不在 b)
a - b # {1, 2}
a.difference(b) # 同上
# 对称差集(只在其中一个)
a ^ b # {1, 2, 5, 6}
a.symmetric_difference(b)
# 子集/超集
{1, 2} <= a # True,子集
a >= {1, 2} # True,超集
7. 集合推导式
squares = {x**2 for x in range(5)}
print(squares) # {0, 1, 4, 9, 16}
8. 创建集合
s = {1, 2, 3} # 字面量
s = set() # 空集合,注意不是 {}(那是空字典)
s = set([1, 2, 2, 3]) # 从列表创建,自动去重 -> {1, 2, 3}
s = set("hello") # 从字符串创建 -> {'h', 'e', 'l', 'o'}
经典坑:{} 是空字典,不是空集合。
print(type({})) # <class 'dict'>
print(type(set())) # <class 'set'>
三、dict 和 set 对比
| 对比项 | dict | set |
|---|---|---|
| 存储内容 | 键值对 | 单个元素 |
| 是否可重复 | 键唯一,值可重复 | 元素唯一 |
| 是否有序 | 有序(3.7+,按插入顺序) | 无序 |
| 底层结构 | 哈希表 | 哈希表 |
| 查找效率 | O(1) | O(1) |
| 元素要求 | 键必须可哈希 | 元素必须可哈希 |
| 可变性 | 可变 | 可变 |
| 空对象写法 | {} | set() |
| 典型用途 | 关联数据、映射 | 去重、成员判断、集合运算 |
四、回答你的两个具体问题
问题 1:dict 有什么特点?
总结成 7 点:
- 键值对存储,按键查找 O(1)。
- 键唯一,值可重复。
- 键必须可哈希(不可变),值任意。
- 有序(Python 3.7+,按插入顺序)。
- 可变,可增删改。
- 支持多种遍历方式(keys、values、items)。
- 支持字典推导式。
问题 2:set 是不能有重复值,有序吗?
不能有重复值:✅ 正确,集合元素唯一。
有序吗:❌ 无序。集合不保证顺序,遍历顺序由哈希值决定,和你插入的顺序无关。
s = {5, 1, 3, 2, 4}
print(s) # 可能是 {1, 2, 3, 4, 5},也可能是别的顺序
如果你需要既去重又保持顺序,不能用 set,要用 dict.fromkeys():
items = [3, 1, 2, 3, 1, 4]
unique_ordered = list(dict.fromkeys(items))
print(unique_ordered) # [3, 1, 2, 4],去重且保持原顺序
这是 Python 3.7+ 的常用技巧,因为字典有序,dict.fromkeys() 会按插入顺序去重。
五、一句话总结
| 问题 | 答案 |
|---|---|
dict 特点? | 键值对、键唯一、键可哈希、有序(3.7+)、O(1) 查找、可变 |
set 能重复吗? | 不能,元素唯一 |
set 有序吗? | 无序,遍历顺序不确定 |
| 想有序去重? | 用 dict.fromkeys(),不要用 set |
| 空集合怎么写? | set(),不是 {} |
| 两者共同点? | 底层都是哈希表,查找 O(1),元素/键必须可哈希 |
记住:dict 是有序的键值对容器,set 是无序的唯一元素容器。两者都靠哈希表实现,所以查找快;但 set 不保证顺序,需要顺序时用 dict 或 dict.fromkeys()。