Python字典详解

字典(Dictionary)是Python中最重要的数据结构之一,用于存储键值对(key-value pairs)。Python字典提供了高效的键值查找机制,是日常开发中最常用的数据结构之一。本文将详细介绍Python字典的特性、操作和最佳实践。

一、字典概述

1. 什么是字典?

字典是Python中的一种无序、可变的映射类型(Mapping Type),用于存储键值对集合。每个键值对之间用逗号,分隔,整个字典用花括号{}包裹。

2. 字典的特点

  • 无序性:Python 3.7之前字典是无序的,Python 3.7及以后版本字典保持插入顺序
  • 可变性:字典创建后可以修改其内容(添加、删除、修改键值对)
  • 映射关系:通过键(key)映射到对应的值(value)
  • 键的唯一性:字典中的键必须唯一
  • 键的可哈希性:字典的键必须是可哈希的(不可变类型,如字符串、数字、元组)
  • 值的任意性:字典的值可以是任意类型(包括可变类型,如列表、字典)

3. 字典的表示

字典使用花括号{}表示,键值对之间用冒号:分隔:

# 字典示例
person = {"name": "张三", "age": 30, "city": "北京"}

# 键可以是不同类型
mixed = {1: "整数键", "字符串键": 2, (1, 2): "元组键"}

# 空字典
empty_dict = {}

二、字典的创建

1. 基本创建方法

使用花括号{}直接创建字典:

# 基本创建方法示例

# 创建空字典
empty = {}

# 创建包含键值对的字典
person = {"name": "李四", "age": 25, "email": "lisi@example.com"}

# 使用不同类型的键
mixed = {
    1: "数字键",
    "字符串键": "字符串值",
    (1, 2): "元组键",
    True: "布尔键"
}

print(empty)  # {}
print(person)  # {'name': '李四', 'age': 25, 'email': 'lisi@example.com'}
print(mixed)  # {1: '布尔键', '字符串键': '字符串值', (1, 2): '元组键'}(注意True和1在字典中视为同一个键)

2. 使用dict()函数创建

dict()函数可以从不同的数据源创建字典:

# 使用dict()函数创建字典示例

# 从关键字参数创建
d = dict(name="王五", age=35, city="上海")
print(d)  # {'name': '王五', 'age': 35, 'city': '上海'}

# 从键值对序列创建
pairs = [("name", "赵六"), ("age", 40), ("city", "广州")]
d = dict(pairs)
print(d)  # {'name': '赵六', 'age': 40, 'city': '广州'}

# 从两个列表创建(使用zip()函数)
keys = ["name", "age", "city"]
values = ["钱七", 45, "深圳"]
d = dict(zip(keys, values))
print(d)  # {'name': '钱七', 'age': 45, 'city': '深圳'}

# 从另一个字典创建(浅拷贝)
original = {"name": "孙八", "age": 50}
d = dict(original)
print(d)  # {'name': '孙八', 'age': 50}
print(d is original)  # False(创建了新字典)

3. 使用字典推导式创建

字典推导式是一种简洁创建字典的方法,语法为{key_expression: value_expression for item in iterable if condition}

# 使用字典推导式创建字典示例

# 基本字典推导式
numbers = [1, 2, 3, 4, 5]
squares = {num: num ** 2 for num in numbers}
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 带有条件的字典推导式
even_squares = {num: num ** 2 for num in numbers if num % 2 == 0}
print(even_squares)  # {2: 4, 4: 16}

# 使用两个列表创建
a = ["name", "age", "city"]
b = ["周九", 55, "杭州"]
person = {k: v for k, v in zip(a, b)}
print(person)  # {'name': '周九', 'age': 55, 'city': '杭州'}

# 转换字符串为字符计数字典
word = "hello"
char_count = {char: word.count(char) for char in word}
print(char_count)  # {'h': 1, 'e': 1, 'l': 2, 'o': 1}

4. 使用fromkeys()方法创建

fromkeys()方法可以从序列创建字典,所有键的初始值相同:

# 使用fromkeys()方法创建字典示例

# 创建所有键值为None的字典
keys = ["name", "age", "city"]
d = dict.fromkeys(keys)
print(d)  # {'name': None, 'age': None, 'city': None}

# 创建所有键值为相同值的字典
d = dict.fromkeys(keys, "默认值")
print(d)  # {'name': '默认值', 'age': '默认值', 'city': '默认值'}

# 注意:如果默认值是可变对象,所有键会共享同一个对象
keys = ["a", "b", "c"]
d = dict.fromkeys(keys, [])
d["a"].append(1)
print(d)  # {'a': [1], 'b': [1], 'c': [1]}(所有键共享同一个列表)

三、字典的基本操作

1. 访问字典元素

可以通过键来访问字典中的值:

# 访问字典元素示例

person = {"name": "吴十", "age": 60, "city": "南京"}

# 使用方括号访问
print(person["name"])  # 吴十
print(person["age"])  # 60

# 访问不存在的键会报错
try:
    print(person["email"])
except KeyError as e:
    print(f"错误:{e}")  # 错误:'email'

# 使用get()方法访问(更安全)
print(person.get("name"))  # 吴十
print(person.get("email"))  # None(键不存在时返回None)
print(person.get("email", "默认邮箱"))  # 默认邮箱(键不存在时返回指定的默认值)

2. 修改字典元素

可以通过键来修改字典中的值:

# 修改字典元素示例

person = {"name": "郑十一", "age": 65, "city": "成都"}

# 修改现有键的值
person["age"] = 70
print(person)  # {'name': '郑十一', 'age': 70, 'city': '成都'}

# 使用update()方法修改多个键值对
person.update({"age": 75, "city": "重庆"})
print(person)  # {'name': '郑十一', 'age': 75, 'city': '重庆'}

# 使用关键字参数修改
person.update(age=80, city="西安")
print(person)  # {'name': '郑十一', 'age': 80, 'city': '西安'}

3. 添加字典元素

可以通过赋值或update()方法添加新的键值对:

# 添加字典元素示例

person = {"name": "王十二", "age": 30}

# 添加新的键值对
person["city"] = "武汉"
print(person)  # {'name': '王十二', 'age': 30, 'city': '武汉'}

# 使用update()方法添加多个键值对
person.update({"email": "wang12@example.com", "phone": "13800138000"})
print(person)  # {'name': '王十二', 'age': 30, 'city': '武汉', 'email': 'wang12@example.com', 'phone': '13800138000'}

# 使用关键字参数添加
person.update(gender="男")
print(person)  # {'name': '王十二', 'age': 30, 'city': '武汉', 'email': 'wang12@example.com', 'phone': '13800138000', 'gender': '男'}

4. 删除字典元素

可以使用多种方法删除字典中的元素:

# 删除字典元素示例

person = {"name": "赵十三", "age": 35, "city": "长沙", "email": "zhao13@example.com"}

# 使用del语句删除
print(person)  # {'name': '赵十三', 'age': 35, 'city': '长沙', 'email': 'zhao13@example.com'}
del person["city"]
print(person)  # {'name': '赵十三', 'age': 35, 'email': 'zhao13@example.com'}

# 删除不存在的键会报错
try:
    del person["phone"]
except KeyError as e:
    print(f"错误:{e}")  # 错误:'phone'

# 使用pop()方法删除(返回被删除的值)
email = person.pop("email")
print(email)  # zhao13@example.com
print(person)  # {'name': '赵十三', 'age': 35}

# pop()方法可以指定默认值(键不存在时返回默认值)
phone = person.pop("phone", "默认电话")
print(phone)  # 默认电话
print(person)  # {'name': '赵十三', 'age': 35}

# 使用popitem()方法删除最后一个键值对(Python 3.7+)
pair = person.popitem()
print(pair)  # ('age', 35)
print(person)  # {'name': '赵十三'}

# 使用clear()方法清空字典
person.clear()
print(person)  # {}

5. 字典的其他基本操作

# 字典的其他基本操作示例

person = {"name": "钱十四", "age": 40, "city": "青岛"}

# 获取字典的长度(键值对数量)
print(len(person))  # 3

# 检查键是否存在
print("name" in person)  # True
print("email" not in person)  # True

# 字典的复制(浅拷贝)
person2 = person.copy()
print(person2)  # {'name': '钱十四', 'age': 40, 'city': '青岛'}
print(person2 is person)  # False

# 字典的合并
person3 = {"email": "qian14@example.com", "phone": "13900139000"}
merged = {**person, **person3}
print(merged)  # {'name': '钱十四', 'age': 40, 'city': '青岛', 'email': 'qian14@example.com', 'phone': '13900139000'}

# Python 3.9+支持使用|运算符合并
# merged = person | person3

四、字典的常用方法

Python字典提供了丰富的方法用于操作字典:

1. keys()、values()和items()

这些方法用于获取字典的键、值和键值对:

# keys()、values()和items()方法示例

person = {"name": "孙十五", "age": 45, "city": "济南"}

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

# 获取所有值
values = person.values()
print(values)  # dict_values(['孙十五', 45, '济南'])
print(list(values))  # ['孙十五', 45, '济南']

# 获取所有键值对
items = person.items()
print(items)  # dict_items([('name', '孙十五'), ('age', 45), ('city', '济南')])
print(list(items))  # [('name', '孙十五'), ('age', 45), ('city', '济南')]

# 遍历字典的键
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. get()方法

get()方法用于获取指定键的值,如果键不存在则返回默认值:

# get()方法示例

person = {"name": "周十六", "age": 50, "city": "福州"}

# 获取存在的键
print(person.get("name"))  # 周十六

# 获取不存在的键(返回None)
print(person.get("email"))  # None

# 获取不存在的键(返回默认值)
print(person.get("email", "no email"))  # no email

# 获取不存在的键(返回默认值)
print(person.get("phone", 1234567890))  # 1234567890

3. update()方法

update()方法用于更新字典中的键值对:

# update()方法示例

person = {"name": "吴十七", "age": 55, "city": "厦门"}

# 使用字典更新
person.update({"age": 60, "email": "wu17@example.com"})
print(person)  # {'name': '吴十七', 'age': 60, 'city': '厦门', 'email': 'wu17@example.com'}

# 使用关键字参数更新
person.update(age=65, phone="13800138000")
print(person)  # {'name': '吴十七', 'age': 65, 'city': '厦门', 'email': 'wu17@example.com', 'phone': '13800138000'}

# 使用键值对序列更新
person.update([("city", "南宁"), ("gender", "男")])
print(person)  # {'name': '吴十七', 'age': 65, 'city': '南宁', 'email': 'wu17@example.com', 'phone': '13800138000', 'gender': '男'}

4. setdefault()方法

setdefault()方法用于获取指定键的值,如果键不存在则添加该键并设置默认值:

# setdefault()方法示例

person = {"name": "郑十八", "age": 70, "city": "昆明"}

# 获取存在的键
name = person.setdefault("name", "默认姓名")
print(name)  # 郑十八
print(person)  # {'name': '郑十八', 'age': 70, 'city': '昆明'}

# 获取不存在的键(添加键并设置默认值)
email = person.setdefault("email", "默认邮箱")
print(email)  # 默认邮箱
print(person)  # {'name': '郑十八', 'age': 70, 'city': '昆明', 'email': '默认邮箱'}

# 获取不存在的键(默认值为None)
phone = person.setdefault("phone")
print(phone)  # None
print(person)  # {'name': '郑十八', 'age': 70, 'city': '昆明', 'email': '默认邮箱', 'phone': None}

5. 其他方法

# 其他方法示例

# 创建字典
d1 = {"a": 1, "b": 2, "c": 3}
d2 = {"c": 4, "d": 5, "e": 6}

# 使用copy()方法复制字典
d3 = d1.copy()
print(d3)  # {'a': 1, 'b': 2, 'c': 3}

# 使用clear()方法清空字典
d3.clear()
print(d3)  # {}

# 使用pop()方法删除键值对
value = d1.pop("a")
print(value)  # 1
print(d1)  # {'b': 2, 'c': 3}

# 使用popitem()方法删除最后一个键值对
pair = d1.popitem()
print(pair)  # ('c', 3)
print(d1)  # {'b': 2}

五、字典的高级操作

1. 嵌套字典

字典中可以包含其他字典,形成嵌套字典:

# 嵌套字典示例

# 创建嵌套字典
student = {
    "name": "王十九",
    "age": 18,
    "scores": {
        "数学": 95,
        "语文": 90,
        "英语": 85
    },
    "address": {
        "city": "贵阳",
        "street": "解放路",
        "zipcode": "550000"
    }
}

# 访问嵌套字典中的元素
print(student["name"])  # 王十九
print(student["scores"]["数学"])  # 95
print(student["address"]["city"])  # 贵阳

# 修改嵌套字典中的元素
student["scores"]["数学"] = 100
print(student["scores"]["数学"])  # 100

# 添加嵌套字典中的元素
student["scores"]["物理"] = 92
print(student["scores"])  # {'数学': 100, '语文': 90, '英语': 85, '物理': 92}

# 遍历嵌套字典
for subject, score in student["scores"].items():
    print(f"{subject}: {score}")

# 遍历整个嵌套字典
for key, value in student.items():
    if isinstance(value, dict):
        print(f"{key}:")
        for k, v in value.items():
            print(f"  {k}: {v}")
    else:
        print(f"{key}: {value}")

2. 字典推导式

字典推导式是一种简洁创建字典的方法:

# 字典推导式示例

# 基本字典推导式
numbers = [1, 2, 3, 4, 5]
squares = {num: num ** 2 for num in numbers}
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 带有条件的字典推导式
even_squares = {num: num ** 2 for num in numbers if num % 2 == 0}
print(even_squares)  # {2: 4, 4: 16}

# 使用两个列表创建
keys = ["name", "age", "city"]
values = ["赵二十", 20, "拉萨"]
person = {k: v for k, v in zip(keys, values)}
print(person)  # {'name': '赵二十', 'age': 20, 'city': '拉萨'}

# 转换字符串为字符计数字典
word = "python"
char_count = {char: word.count(char) for char in word}
print(char_count)  # {'p': 1, 'y': 1, 't': 1, 'h': 1, 'o': 1, 'n': 1}

# 反转字典键值对
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'}

# 注意:如果原字典的值不唯一,反转后会丢失一些键值对
original = {"a": 1, "b": 2, "c": 1}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict)  # {1: 'c', 2: 'b'}('a'键丢失)

3. 字典的排序

可以使用sorted()函数对字典进行排序:

# 字典的排序示例

# 创建字典
scores = {"数学": 95, "语文": 90, "英语": 85, "物理": 92, "化学": 88}

# 按键排序
for key in sorted(scores.keys()):
    print(f"{key}: {scores[key]}")

# 按键排序并创建新字典
keys_sorted = {k: scores[k] for k in sorted(scores.keys())}
print(keys_sorted)  # {'化学': 88, '物理': 92, '数学': 95, '英语': 85, '语文': 90}

# 按值排序
for key, value in sorted(scores.items(), key=lambda x: x[1]):
    print(f"{key}: {value}")

# 按值降序排序
for key, value in sorted(scores.items(), key=lambda x: x[1], reverse=True):
    print(f"{key}: {value}")

# 按值的绝对值排序
scores = {"a": -5, "b": 3, "c": -1, "d": 2}
for key, value in sorted(scores.items(), key=lambda x: abs(x[1])):
    print(f"{key}: {value}")

4. 字典的合并

Python提供了多种合并字典的方法:

# 字典的合并示例

dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}

# 方法1:使用update()方法(会修改原字典)
dict1_copy = dict1.copy()
dict1_copy.update(dict2)
print(dict1_copy)  # {'a': 1, 'b': 3, 'c': 4}

# 方法2:使用**运算符(Python 3.5+)
merged = {**dict1, **dict2}
print(merged)  # {'a': 1, 'b': 3, 'c': 4}

# 方法3:使用字典推导式
merged = {k: v for d in [dict1, dict2] for k, v in d.items()}
print(merged)  # {'a': 1, 'b': 3, 'c': 4}

# 方法4:使用chain()函数(from itertools)
from itertools import chain
merged = dict(chain(dict1.items(), dict2.items()))
print(merged)  # {'a': 1, 'b': 3, 'c': 4}

# 方法5:使用|运算符(Python 3.9+)
# merged = dict1 | dict2
# print(merged)  # {'a': 1, 'b': 3, 'c': 4}

5. 字典的键值对转换

可以将字典转换为其他数据结构:

# 字典的键值对转换示例

person = {"name": "钱二十一", "age": 21, "city": "银川"}

# 转换为列表
keys_list = list(person.keys())
values_list = list(person.values())
items_list = list(person.items())

print(keys_list)  # ['name', 'age', 'city']
print(values_list)  # ['钱二十一', 21, '银川']
print(items_list)  # [('name', '钱二十一'), ('age', 21), ('city', '银川')]

# 转换为元组
keys_tuple = tuple(person.keys())
values_tuple = tuple(person.values())
items_tuple = tuple(person.items())

print(keys_tuple)  # ('name', 'age', 'city')
print(values_tuple)  # ('钱二十一', 21, '银川')
print(items_tuple)  # (('name', '钱二十一'), ('age', 21), ('city', '银川'))

# 转换为集合
keys_set = set(person.keys())
values_set = set(person.values())

print(keys_set)  # {'name', 'age', 'city'}
print(values_set)  # {'钱二十一', 21, '银川'}

六、字典的性能分析

1. 时间复杂度

字典的核心优势是高效的键值查找,主要操作的时间复杂度如下:

操作 时间复杂度 描述
访问元素 O(1) 通过键访问值
修改元素 O(1) 通过键修改值
添加元素 O(1) 添加新的键值对
删除元素 O(1) 删除键值对
成员检查 O(1) 检查键是否存在
获取所有键 O(n) 获取字典的所有键
获取所有值 O(n) 获取字典的所有值
获取所有键值对 O(n) 获取字典的所有键值对

2. 性能优化建议

  • 尽量使用不可变类型作为键(字符串、数字、元组)
  • 避免使用可变类型作为键(列表、字典)
  • 使用get()方法而不是[]访问元素(避免KeyError)
  • 对于频繁查找的场景,字典比列表更高效
  • 对于需要保持插入顺序的场景,使用Python 3.7+的字典

3. 字典与列表的性能比较

# 字典与列表的性能比较示例

import time

# 创建大列表和大字典
n = 1000000
my_list = list(range(n))
my_dict = {i: i for i in range(n)}

# 测试列表的查找性能
start = time.time()
for i in range(n):
    if i == n - 1:
        pass
end = time.time()
print(f"列表查找耗时:{end - start:.6f}秒")

# 测试字典的查找性能
start = time.time()
for i in range(n):
    if i in my_dict:
        pass
end = time.time()
print(f"字典查找耗时:{end - start:.6f}秒")

七、字典的最佳实践

1. 适用场景

  • 需要通过键快速查找值的场景
  • 需要存储键值对集合的场景
  • 需要表示对象属性的场景
  • 需要缓存数据的场景
  • 需要存储配置信息的场景

2. 最佳实践

# 最佳实践示例

# 1. 使用有意义的键名
# 不好的做法
user = {"n": "张三", "a": 30, "c": "北京"}

# 好的做法
user = {"name": "张三", "age": 30, "city": "北京"}

# 2. 使用get()方法访问元素(避免KeyError)
# 不好的做法
try:
    email = user["email"]
except KeyError:
    email = ""

# 好的做法
email = user.get("email", "")

# 3. 使用字典推导式创建字典
# 不好的做法
numbers = [1, 2, 3, 4, 5]
squares = {}
for num in numbers:
    squares[num] = num ** 2

# 好的做法
squares = {num: num ** 2 for num in numbers}

# 4. 避免使用可变类型作为键
# 不好的做法
try:
    bad_dict = {[1, 2]: "值"}
except TypeError as e:
    print(f"错误:{e}")  # 错误:unhashable type: 'list'

# 好的做法
good_dict = {(1, 2): "值"}  # 使用元组作为键

# 5. 合理使用嵌套字典
# 好的做法
student = {
    "name": "李四",
    "age": 20,
    "scores": {
        "数学": 95,
        "语文": 90,
        "英语": 85
    }
}

# 6. 使用zip()函数创建字典
# 好的做法
keys = ["name", "age", "city"]
values = ["王五", 25, "上海"]
person = dict(zip(keys, values))

3. 常见错误

# 常见错误示例

# 错误1:使用可变类型作为键
try:
    d = {[1, 2]: "值"}
except TypeError as e:
    print(f"错误:{e}")  # 错误:unhashable type: 'list'

# 错误2:访问不存在的键
try:
    d = {"a": 1, "b": 2}
    print(d["c"])
except KeyError as e:
    print(f"错误:{e}")  # 错误:'c'

# 错误3:修改不可哈希的键
try:
    d = {(1, 2): "值"}
    d[(1, 2)][0] = 3
except TypeError as e:
    print(f"错误:{e}")  # 错误:'tuple' object does not support item assignment

# 错误4:混淆字典的键和值
d = {"a": 1, "b": 2}
# 不好的做法
for value in d:
    print(value)  # 输出键,不是值

# 好的做法
for key in d:
    print(d[key])  # 输出值

for value in d.values():
    print(value)  # 输出值

八、与其他数据结构的比较

特性 字典 列表 元组
存储方式 键值对 元素序列 元素序列
访问方式 通过键 通过索引 通过索引
可变性 可变 可变 不可变
有序性 Python 3.7+有序 有序 有序
键的唯一性 键必须唯一 元素可以重复 元素可以重复
可哈希性 键必须可哈希 不可哈希 可哈希
查找效率 O(1) O(n) O(n)
内存占用 较大 较小 较小
适用场景 键值映射、快速查找 有序元素集合、需要频繁修改 不可变元素集合、需要作为字典键

九、总结

字典是Python中最强大、最灵活的数据结构之一,具有以下特点:

  1. 高效的键值查找:字典的核心优势是O(1)时间复杂度的键值查找
  2. 灵活的键值对存储:可以存储任意类型的键值对
  3. 可变性:可以动态添加、修改、删除键值对
  4. 丰富的操作方法:提供了大量用于操作字典的方法
  5. 广泛的应用场景:在日常开发中几乎无处不在

通过掌握Python字典的特性和操作方法,可以编写出更高效、更优雅的代码。在实际开发中,应根据具体需求选择合适的数据结构,充分发挥字典的优势。


发布网站:荣殿教程(zhangrongdian.com)

作者:张荣殿