字典创建与访问
字典(dict)是 Python 唯一的内置映射类型,通过**键(key)**而非整数索引来存取数据。字典的查找、插入和删除操作的平均时间复杂度为 O(1),这使其成为处理关联数据的首选结构。
创建字典
花括号 {} 是字典的字面量语法,内部以 key: value 形式书写键值对:
# 字面量创建
tel = {'jack': 4098, 'sape': 4139}
# 空字典
empty = {}
empty = dict()
注意 {} 创建的是空字典而非空集合。要创建空集合,必须使用 set()。
dict() 构造函数支持多种初始化方式。传入键值对序列时,自动构建字典:
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
# {'sape': 4139, 'guido': 4127, 'jack': 4098}
当键是简单字符串时,使用关键字参数更直观:
dict(sape=4139, guido=4127, jack=4098)
# {'sape': 4139, 'guido': 4127, 'jack': 4098}
关键字参数的限制是键必须符合标识符规则(不能包含空格、不能以数字开头等)。不符合时,回退到字面量或序列方式。
键值访问与修改
通过方括号语法 d[key] 访问值。键不存在时抛出 KeyError:
tel = {'jack': 4098, 'sape': 4139}
print(tel['jack']) # 4098
print(tel['irv']) # KeyError: 'irv'
赋值操作在键存在时覆盖旧值,键不存在时创建新键值对:
tel['guido'] = 4127 # 新增
tel['jack'] = 9999 # 覆盖
del 语句按键删除键值对:
del tel['sape']
成员运算符 in 和 not in 检测键是否存在,这是避免 KeyError 的安全手段:
'guido' in tel # True
'jack' not in tel # False
键的类型要求
字典键必须是**可哈希(hashable)**的对象。可哈希对象需同时满足两个条件:具有 __hash__() 方法,且具有 __eq__() 方法。不可变类型如字符串、数字、元组(仅当其元素也均为不可变类型时)都可以作为键。列表、字典、集合等可变对象不能作为键:
d = {}
d['string'] = 1 # ✅
d[42] = 2 # ✅
d[(1, 2)] = 3 # ✅,元组元素均为不可变类型
d[[1, 2]] = 4 # ❌ TypeError: unhashable type: 'list'
d[{'a': 1}] = 5 # ❌ TypeError: unhashable type: 'dict'
元组作为键时,其直接或间接包含的所有元素都必须是不可变类型。如果元组内嵌套了列表,同样不可哈希:
d[(1, [2, 3])] = 1 # TypeError: unhashable type: 'list'
d[(1, (2, 3))] = 1 # ✅,完全嵌套不可变类型
哈希原理简述
字典底层基于哈希表实现。插入键值对时,Python 调用键的 __hash__() 方法计算哈希值,确定存储位置;查找时,通过哈希值直接定位桶(bucket),再用 __eq__() 比较确认键的精确匹配。
哈希值在对象生命周期内应保持不变,因此可变对象不能作为键——如果列表内容改变,其等效性不变但哈希值无法更新,会导致字典内部状态混乱。
自定义类实例默认是可哈希的(基于 id()),但如果重写了 __eq__() 而未定义 __hash__(),Python 会自动将 __hash__ 设为 None,使实例不可哈希:
class Person:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
p = Person("Alice")
d = {p: 1} # TypeError: unhashable type: 'Person'
字典的顺序
Python 3.7 起,字典保留插入顺序:遍历键值对时,顺序与插入时一致。这是语言实现层面的保证,在 Python 3.7 作为实现细节引入,3.8 起写入语言规范:
d = {}
d['a'] = 1
d['b'] = 2
d['c'] = 3
list(d) # ['a', 'b', 'c'],按插入顺序
但顺序保留不等于排序。sorted(d) 返回按键排序的新列表,不改变字典内部顺序:
sorted(d) # ['a', 'b', 'c'](本例恰好相同)
重复赋值同一键不会破坏顺序,该键保持在最初插入的位置:
d = {'b': 2, 'a': 1}
d['b'] = 20
list(d) # ['b', 'a'],'b' 仍在第一位
视图对象
list(d) 返回字典所有键的列表。更常用的是 keys()、values()、items() 方法,它们返回动态视图对象(view object),反映字典的实时变化:
d = {'jack': 4098, 'sape': 4139}
keys = d.keys()
print(keys) # dict_keys(['jack', 'sape'])
d['guido'] = 4127
print(keys) # dict_keys(['jack', 'sape', 'guido']),自动反映新增
视图对象支持集合运算(如 &、|、-),可用于快速比较两个字典的键集合。