字典(dict)是 Python 中最核心、最常用的数据结构之一,它以键值对(Key-Value Pair) 的形式存储数据,具备高效查找、灵活修改的特性,广泛应用于配置存储、数据映射、计数统计等场景。

一、字典的定义与核心特性

1. 基本定义

字典是一种可变、无序(Python 3.7+ 后变为插入有序)、可嵌套的数据类型,每个元素由 键(Key) 和 值(Value) 组成,格式为:

{key1: value1, key2: value2, ..., keyn: valuen}
  • 键(Key):必须是不可变类型(如 strinttuplebool),且唯一(重复键会覆盖前值);
  • 值(Value):可以是任意类型(如 intstrlistdict 甚至函数),无唯一性要求;
  • 分隔符:键与值用 : 分隔,键值对之间用 , 分隔,整体用 {} 包裹。

2. 核心特性

特性 说明
可变(Mutable) 可动态添加、修改、删除键值对(但键不可修改,需通过 “删除旧键 + 添加新键” 实现)
插入有序(3.7+) 3.7 版本后,字典会保留键值对的插入顺序,遍历顺序与插入顺序一致
高效查找 基于哈希表实现,查找键的时间复杂度为 O(1)(远快于列表的 O (n))
可嵌套 值可以是另一个字典,支持多层嵌套(如 {'a': {'b': 1}}
不可哈希 字典本身是可变类型,不可作为其他字典的键(但可作为值)

二、字典的创建方式

Python 提供多种创建字典的方法,适用于不同场景:

1. 直接使用 {}(最常用)

适合已知固定键值对的场景:

# 基础字典(键为字符串、整数)
user = {"name": "Alice", "age": 25, "is_student": False}

# 键为元组(不可变类型,合法)
point = {(1, 2): "原点", (3, 4): "终点"}

# 键为布尔值(True=1,False=0,注意重复覆盖)
bool_dict = {True: "真", 1: "整数1"}  # 结果:{True: "整数1"}(True与1哈希值相同,覆盖)

2. 使用 dict() 构造函数

适合从其他数据结构(如列表、元组)转换为字典:

# 1. 传入键值对参数(键无需引号,本质是关键字参数)
user = dict(name="Bob", age=30, is_student=True)

# 2. 传入可迭代对象(每个元素是长度为2的子迭代对象,如列表、元组)

# 方式1
list_data = [["name", "Charlie"], ["age", 28]]
user = dict(list_data)  # 结果:{"name": "Charlie", "age": 28}

# 方式2
fields = ["name", "age"]
values = ["Charlie", 28]
user = dict(zip(fields, values))

# 3. 传入字典(用于复制,浅拷贝)
old_dict = {"a": 1}
new_dict = dict(old_dict)  # 结果:{"a": 1}

3. 字典推导式(高效创建)

类似列表推导式,通过循环和条件过滤动态生成字典,简洁高效:

# 1. 基础推导式:键值对映射
numbers = [1, 2, 3]
square_dict = {num: num**2 for num in numbers}  # 结果:{1:1, 2:4, 3:9}

# 2. 带条件过滤:只保留偶数的平方
even_square_dict = {num: num**2 for num in numbers if num % 2 == 0}  # 结果:{2:4}

# 3. 双循环推导式:从两个列表生成字典
keys = ["a", "b"]
values = [10, 20]
pair_dict = {k: v for k, v in zip(keys, values)}  # 结果:{"a":10, "b":20}

4. 使用 dict.fromkeys()(批量创建默认值)

适合批量创建键,且所有键的初始值相同的场景(注意:值是引用类型时,修改一个会影响所有):

# 1. 基础用法:值为不可变类型(安全)
keys = ["x", "y", "z"]
default_dict = dict.fromkeys(keys, 0)  # 结果:{"x":0, "y":0, "z":0}

# 2. 注意:值为可变类型(如列表)时,所有键共享同一个列表
danger_dict = dict.fromkeys(keys, [])
danger_dict["x"].append(1)  # 结果:{"x":[1], "y":[1], "z":[1]}(所有键的列表都被修改)

# 解决方法:用推导式避免共享引用
safe_dict = {k: [] for k in keys}
safe_dict["x"].append(1)  # 结果:{"x":[1], "y":[], "z":[]}(仅修改x的列表)

三、字典的核心操作(增删改查)

1. 访问键值对(查)

字典的访问围绕 “键” 展开,有两种核心方式:

(1)通过 dict[key] 访问(直接访问)

  • 优点:语法简洁;
  • 缺点:若键不存在,会抛出 KeyError 异常。
user = {"name": "Alice", "age": 25}
print(user["name"])  # 输出:Alice
print(user["gender"])  # 报错:KeyError: 'gender'

(2)通过 dict.get(key, default) 访问(安全访问)

  • 优点:键不存在时返回 default(默认是 None),不报错;
  • 场景:不确定键是否存在时(如处理用户输入、配置文件)。
print(user.get("name"))  # 输出:Alice
print(user.get("gender", "未知"))  # 输出:未知(键不存在,返回默认值)
print(user.get("gender"))  # 输出:None(无默认值时返回None)

2. 添加 / 修改键值对(增 / 改)

字典的键是唯一的,因此 “添加” 和 “修改” 共用同一语法:

  • 若键不存在:执行 “添加” 操作;
  • 若键已存在:执行 “修改” 操作(覆盖旧值)。
user = {"name": "Alice", "age": 25}

# 1. 修改已有键(age)
user["age"] = 26  # 结果:{"name": "Alice", "age": 26}

# 2. 添加新键(gender)
user["gender"] = "female"  # 结果:{"name": "Alice", "age": 26, "gender": "female"}

# 3. 批量添加/修改:使用 dict.update()
user.update({"city": "Beijing", "age": 27})  # 批量操作:修改age,添加city
print(user)  # 输出:{"name": "Alice", "age": 27, "gender": "female", "city": "Beijing"}

# update() 也支持键值对参数
user.update(city="Shanghai", is_student=False)
print(user)  # 输出:{"name": "Alice", "age": 27, "gender": "female", "city": "Shanghai", "is_student": False}

3. 删除键值对(删)

Python 提供多种删除字典元素的方法,适用于不同需求:

方法 作用 返回值 注意事项
del dict[key] 删除指定键值对 无返回值 键不存在时抛 KeyError
dict.pop(key) 删除指定键值对并返回对应的值 返回被删除的值 键不存在时抛 KeyError(可加默认值:pop(key, default)
dict.popitem() 删除最后一个键值对(3.7+ 插入顺序) 返回被删除的键值对(元组) 字典为空时抛 KeyError
dict.clear() 清空字典所有键值对 无返回值 字典变为空字典({}
user = {"name": "Alice", "age": 25, "gender": "female"}

# 1. del:删除指定键
del user["gender"]
print(user)  # 输出:{"name": "Alice", "age": 25}

# 2. pop():删除并返回值
age = user.pop("age")
print(age)    # 输出:25
print(user)   # 输出:{"name": "Alice"}

# pop() 键不存在时用默认值
city = user.pop("city", "Unknown")
print(city)   # 输出:Unknown(键不存在,返回默认值)

# 3. popitem():删除最后一个键值对
name_pair = user.popitem()
print(name_pair)  # 输出:("name", "Alice")
print(user)       # 输出:{}(空字典)

# 4. clear():清空字典
user = {"a": 1}
user.clear()
print(user)  # 输出:{}

四、字典的遍历

字典的遍历可针对 “键”“值”“键值对” 三种维度,核心依赖 keys()values()items() 三个方法(返回的是视图对象,不占用额外内存,会随原字典动态更新)。

1. 遍历键(dict.keys()

user = {"name": "Alice", "age": 25}

# 方式1:直接遍历字典(默认遍历键)
for key in user:
    print(key)  # 输出:name, age

# 方式2:显式调用 keys()(更清晰)
for key in user.keys():
    print(key)  # 输出:name, age

2. 遍历值(dict.values()

for value in user.values():
    print(value)  # 输出:Alice, 25

3. 遍历键值对(dict.items()

最常用的遍历方式,返回键值对元组((key, value)),可通过解包直接获取键和值:

for key, value in user.items():
    print(f"{key}: {value}")  # 输出:name: Alice, age: 25

4. 遍历的效率对比

  • 遍历键 / 值 / 键值对的时间复杂度均为 O(n)(n 为字典长度);
  • 视图对象(keys()/values()/items())不生成新列表,比 list(dict.keys()) 更高效(避免内存浪费)。

五、字典的常用方法

除了上述增删改查和遍历方法,字典还有一些高频实用方法:

方法 作用 示例
dict.copy() 浅拷贝字典(值为可变类型时仅复制引用) new_dict = old_dict.copy()
dict.setdefault(key, default) 若键存在则返回值,不存在则添加键值对(默认值为 None user.setdefault("city", "Beijing")
dict.__contains__(key) 判断键是否在字典中(等价于 key in dict "name" in user(返回 True

1. dict.copy()(浅拷贝)

  • 作用:创建字典的副本,修改副本不会影响原字典(但值为可变类型时,副本和原字典共享该值的引用);
  • 深拷贝:若值包含嵌套字典 / 列表,需用 copy.deepcopy()(需导入 copy 模块)。
# 浅拷贝示例
old_dict = {"name": "Alice", "hobbies": ["reading", "running"]}
new_dict = old_dict.copy()

# 修改不可变类型(name):不影响原字典
new_dict["name"] = "Bob"
print(old_dict["name"])  # 输出:Alice

# 修改可变类型(hobbies):原字典也会被修改(共享引用)
new_dict["hobbies"].append("swimming")
print(old_dict["hobbies"])  # 输出:["reading", "running", "swimming"]

# 深拷贝(解决嵌套可变类型问题)
import copy
deep_new_dict = copy.deepcopy(old_dict)
deep_new_dict["hobbies"].append("dancing")
print(old_dict["hobbies"])  # 输出:["reading", "running", "swimming"](原字典不变)

选择哪种拷贝方式取决于你的字典是否包含嵌套的可变对象。如果只是简单的字典(值都是不可变类型),浅拷贝就足够了;如果有嵌套结构,可能需要深拷贝。

2. dict.setdefault()(添加默认值)

  • 场景:确保某个键存在(若不存在则添加默认值),常用于统计场景(如单词计数)。
# 单词计数示例
words = ["apple", "banana", "apple"]
count_dict = {}

for word in words:
    # 若word不存在,添加count_dict[word] = 0;若存在,返回当前值
    count_dict.setdefault(word, 0)
    count_dict[word] += 1

print(count_dict)  # 输出:{"apple": 2, "banana": 1}

六、字典的进阶特性

1. 有序性(Python 3.7+)

  • 3.7 版本前,字典是无序的,遍历顺序与插入顺序无关;
  • 3.7 版本后,字典保证插入顺序popitem() 固定删除最后一个插入的键值对;
  • 与 collections.OrderedDict 的区别:3.7+ 普通字典的有序性已与 OrderedDict 一致,且性能更优(OrderedDict 仅保留兼容旧代码和额外功能,如 move_to_end())。

2. 键的要求(不可变 + 哈希 able)

字典的键必须满足 hashable(可哈希),即:

  • 不可变类型:strintfloattuple(元组内元素也需可哈希)、bool
  • 不可用可变类型:listdictset(这些类型不可哈希,无法作为键)。
# 合法键:元组(不可变)
valid_dict = {(1, 2): "valid"}

# 非法键:列表(可变)
invalid_dict = {[1, 2]: "invalid"}  # 报错:TypeError: unhashable type: 'list'

3. 嵌套字典

字典的值可以是另一个字典,支持多层嵌套(常用于存储复杂结构数据,如用户信息、配置文件):

# 嵌套字典:用户信息(包含地址子字典)
user = {
    "name": "Alice",
    "age": 25,
    "address": {
        "city": "Beijing",
        "street": "Main Road",
        "zipcode": "100000"
    }
}

# 访问嵌套值
print(user["address"]["city"])  # 输出:Beijing

# 修改嵌套值
user["address"]["zipcode"] = "100001"
print(user["address"]["zipcode"])  # 输出:100001

# 添加嵌套键
user["address"]["district"] = "Haidian"
print(user["address"])  # 输出:{"city": "Beijing", "street": "Main Road", "zipcode": "100001", "district": "Haidian"}

七、字典的性能与应用场景

1. 性能优势

  • 查找快:基于哈希表实现,查找键的时间复杂度为 O (1)(列表查找需 O (n));
  • 修改快:添加 / 删除键值对的时间复杂度为 O (1)(列表插入 / 删除需 O (n))。

2. 性能劣势

  • 内存占用高:需存储键值对和哈希表结构,内存占用比列表大;
  • 有序性开销:3.7+ 有序字典需维护插入顺序,比无序字典(3.6-)略慢(但日常场景可忽略)。

3. 典型应用场景

(1)配置参数存储

# 应用配置
config = {
    "debug": True,
    "timeout": 30,
    "database": {
        "host": "localhost",
        "port": 3306,
        "name": "mydb"
    }
}

(2)数据计数统计

# 统计字符串中字符出现次数
text = "hello world"
char_count = {}
for char in text:
    char_count[char] = char_count.get(char, 0) + 1
print(char_count)  # 输出:{'h':1, 'e':1, 'l':3, 'o':2, ' ':1, 'w':1, 'r':1, 'd':1}

(3)函数关键字参数(**kwargs

用于接收任意数量的关键字参数:

python

def print_user(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_user(name="Alice", age=25, gender="female")
# 输出:
# name: Alice
# age: 25
# gender: female

(4)数据映射与转换

# 性别编码转换(1→男,2→女)
gender_code = {1: "男", 2: "女"}
user_data = [{"name": "Bob", "gender_code": 1}, {"name": "Lily", "gender_code": 2}]

# 转换为可读性别
for user in user_data:
    user["gender"] = gender_code[user["gender_code"]]
    del user["gender_code"]

print(user_data)
# 输出:[{"name": "Bob", "gender": "男"}, {"name": "Lily", "gender": "女"}]

八、注意事项

  1. 键的唯一性:重复添加相同键会覆盖旧值,如 {"a":1, "a":2} 最终为 {"a":2}
  2. 键的不可变类型:避免用列表、字典等可变类型作为键,否则会抛 TypeError
  3. 浅拷贝陷阱copy() 和 dict() 是浅拷贝,嵌套可变值会共享引用,需用 copy.deepcopy() 深拷贝;
  4. 空字典判断:判断字典是否为空用 if not dict(而非 if dict == {}),更高效简洁;
  5. 视图对象动态性keys()/values()/items() 返回的视图对象会随原字典更新,如:
    d = {"a":1}
    keys = d.keys()
    d["b"] = 2
    print(keys)  # 输出:dict_keys(['a', 'b'])(视图对象自动更新)
    

九、与其他类似结构的对比

数据结构 特点 适用场景
字典(dict 键值对、无序(3.7 + 有序)、可变 高效查找、配置存储、计数统计
有序字典(OrderedDict 3.7- 保证有序,3.7+ 与普通字典一致 兼容旧代码、需 move_to_end() 等特殊功能
defaultdict(collections.defaultdict 键不存在时自动生成默认值(需指定类型) 嵌套字典、批量统计(避免 get() 重复代码)
列表(list 索引访问、有序、可变 有序数据存储、遍历(查找效率低)

例如,defaultdict 简化嵌套统计:

from collections import defaultdict

# 用 defaultdict 统计每个单词的出现位置
text = "hello world hello python"
word_positions = defaultdict(list)  # 键不存在时自动生成空列表

for idx, word in enumerate(text.split()):
    word_positions[word].append(idx)

print(word_positions)
# 输出:defaultdict(<class 'list'>, {'hello': [0, 2], 'world': [1], 'python': [3]})

总结

字典是 Python 中功能最强大的数据结构之一,其键值对映射高效查找特性使其成为处理结构化数据的首选。掌握字典的创建、增删改查、遍历和进阶用法,能极大提升代码的简洁性和效率。在实际开发中,需根据场景选择合适的字典类型(如普通字典、defaultdict),并注意键的不可变特性和浅拷贝陷阱。