Python四大容器(列表/元组/字典/集合)核心操作全面指南:增删改查与其它
Python中的四大核心容器——列表(list)、元组(tuple)、字典(dict)、集合(set),是数据存储与处理的基石。它们各自具备独特的特性(有序/无序、可变/不可变、是否允许重复元素),对应的操作逻辑也存在显著差异。本文将从"增删改查"核心操作入手,延伸至排序、去重、嵌套处理等常用场景,全方位拆解四大容器的用法细节,兼顾基础入门与进阶实战,尽可能确保面面俱到。
一、列表(list):有序可变的"动态数组"
列表是Python中最灵活的容器,有序、可变、允许重复元素,支持任意数据类型的存储(数字、字符串、容器等),底层基于动态数组实现,适用于需要频繁修改数据的场景。
(一)核心操作:增删改查
1. 增:添加元素(4种常用方式)
append(x):在列表末尾添加单个元素x(最常用),时间复杂度O(1)。
lst = [1, 2, 3]
lst.append(4) # 结果:[1, 2, 3, 4]
lst.append([5, 6]) # 支持嵌套,结果:[1, 2, 3, 4, [5, 6]]
extend(iterable):在列表末尾批量添加可迭代对象(如列表、元组、字符串)的所有元素,时间复杂度O(k)(k为可迭代对象长度)。
lst = [1, 2, 3]
lst.extend([4, 5]) # 批量添加列表,结果:[1, 2, 3, 4, 5]
lst.extend(("a", "b")) # 批量添加元组,结果:[1, 2, 3, 4, 5, 'a', 'b']
lst.extend("cd") # 批量添加字符串(按字符拆分),结果:[1, 2, 3, 4, 5, 'a', 'b', 'c', 'd']
insert(index, x):在指定索引index处插入元素x,后续元素自动后移,时间复杂度O(n)(需移动元素)。若索引超出列表长度,默认插入到末尾。
lst = [1, 2, 3]
lst.insert(0, 0) # 在开头插入,结果:[0, 1, 2, 3]
lst.insert(2, "插入元素") # 在索引2处插入,结果:[0, 1, '插入元素', 2, 3]
lst.insert(10, 4) # 索引10超出长度,插入到末尾 → [0, 1, '插入元素', 2, 3, 4]
- 列表拼接(
+):通过+运算符拼接两个列表,返回新列表(原列表不变),时间复杂度O(n+k)。lst1 = [1, 2] lst2 = [3, 4] new_lst = lst1 + lst2 # 结果:[1, 2, 3, 4](lst1、lst2仍为原数据)
2. 删:删除元素(5种常用方式)
remove(x):删除列表中第一个出现的元素x,无返回值,时间复杂度O(n)。
lst = [1, 2, 3, 2]
lst.remove(2) # 删除第一个2,结果:[1, 3, 2]
# lst.remove(4) # 元素不存在时报错:ValueError: list.remove(x): x not in list
pop(index=-1):删除指定索引处的元素(默认删除末尾元素),返回被删除的元素,时间复杂度O(n)(删除非末尾元素需移动数据)。
lst = [1, 2, 3, 4]
lst.pop() # 删除末尾元素4,返回4,结果:[1, 2, 3]
lst.pop(1) # 删除索引1处的2,返回2,结果:[1, 3]
del语句 :根据索引或切片删除元素,支持批量删除,无返回值。
lst = [1, 2, 3, 4, 5]
del lst[0] # 删除索引0处的元素,结果:[2, 3, 4, 5]
del lst[1:3] # 切片删除(左闭右开),结果:[2, 5]
del lst # 直接删除整个列表(后续无法访问)
clear():清空列表中所有元素,保留列表对象(仅删除内容),时间复杂度O(n)。
lst = [1, 2, 3]
lst.clear() # 结果:[](列表仍存在,内容为空)
- 列表推导式(过滤删除):通过条件筛选保留元素,间接实现"删除特定元素",适合批量删除符合条件的元素。
lst = [1, 2, 3, 4, 5] lst = [x for x in lst if x % 2 == 0] # 保留偶数,间接删除奇数,结果:[2, 4]
3. 改:修改元素(2种核心方式)
-
直接索引赋值:通过"索引=新值"修改指定位置的元素,时间复杂度O(1)。
lst = [1, 2, 3] lst[0] = 10 # 修改索引0处的元素,结果:[10, 2, 3] lst[-1] = 30 # 支持负索引(末尾元素),结果:[10, 2, 30] -
切片赋值:通过切片批量替换元素(可改变列表长度),时间复杂度O(k)(k为替换后切片的长度)。注意:当步长不为1时,替换元素个数需与切片长度一致。
lst = [1, 2, 3, 4, 5] lst[1:3] = [20, 30] # 替换索引1-2的元素,结果:[1, 20, 30, 4, 5] lst[3:] = [40, 50, 60] # 替换末尾元素并扩展长度,结果:[1, 20, 30, 40, 50, 60] lst[2:4] = [] # 切片赋值为空列表,等价于删除该区间元素,结果:[1, 20, 50, 60] # 步长不为1的情况 lst = [1, 2, 3, 4, 5] # lst[::2] = [10, 20] # 报错:ValueError: attempt to assign sequence of size 2 to extended slice of size 3 lst[::2] = [10, 20, 30] # 正确,切片长度3,替换元素个数3 → [10,2,20,4,30]
4. 查:查找元素(4种常用方式)
-
索引查找(
lst[index]):通过索引直接获取元素,支持正索引(从0开始)和负索引(从-1开始),时间复杂度O(1)。lst = [1, 2, 3, 4] print(lst[2]) # 正索引2 → 3 print(lst[-2]) # 负索引-2 → 3(倒数第二个元素) -
切片查找(
lst[start:end:step]):通过切片获取子列表,支持步长控制,返回新列表(原列表不变),时间复杂度O(k)。lst = [1, 2, 3, 4, 5, 6] print(lst[1:4]) # 索引1-3 → [2, 3, 4] print(lst[::2]) # 步长2(隔一个取一个)→ [1, 3, 5] print(lst[::-1]) # 步长-1(反转列表)→ [6, 5, 4, 3, 2, 1] -
index(x, start=0, end=len(lst)):查找元素x在列表中第一个出现的索引,支持指定查找范围(start到end),返回索引值,时间复杂度O(n)。
lst = [1, 2, 3, 2, 4]
print(lst.index(2)) # 第一个2的索引 → 1
print(lst.index(2, 2)) # 从索引2开始查找2 → 3
# print(lst.index(5)) # 元素不存在报错:ValueError: 5 is not in list
count(x):统计元素x在列表中出现的次数,返回整数,时间复杂度O(n)。
lst = [1, 2, 3, 2, 2, 4]
print(lst.count(2)) # 2出现3次 → 3
print(lst.count(5)) # 元素不存在 → 0
(二)常用进阶操作
1. 排序与反转
sort(key=None, reverse=False):对列表原地排序(修改原列表),支持自定义排序规则(key参数),默认升序(reverse=False),时间复杂度O(n log n)。注意:仅支持同类型元素排序。
lst = [3, 1, 4, 1, 5]
lst.sort() # 升序排序 → [1, 1, 3, 4, 5]
lst.sort(reverse=True) # 降序排序 → [5, 4, 3, 1, 1]
# 自定义key(按字符串长度排序)
str_lst = ["apple", "banana", "cherry", "date"]
str_lst.sort(key=len) # 按长度升序 → ["date", "apple", "banana", "cherry"]
# 类型不一致会报错
# lst = [1, "2", 3]
# lst.sort() # 报错:TypeError: '<' not supported between instances of 'str' and 'int'
sorted(lst, key=None, reverse=False):返回排序后的新列表(原列表不变),用法与sort()一致。
lst = [3, 1, 4]
new_lst = sorted(lst) # 新列表[1, 3, 4],原列表仍为[3, 1, 4]
reverse():将列表原地反转(修改原列表),时间复杂度O(n)。
lst = [1, 2, 3]
lst.reverse() # 结果 → [3, 2, 1]
2. 列表复制(避免浅拷贝陷阱)
-
浅拷贝(
copy()/切片[:]):复制列表外层元素,内层嵌套容器仍为引用(修改内层会影响原列表)。lst = [1, [2, 3], 4] lst1 = lst.copy() # 浅拷贝 lst2 = lst[:] # 切片实现浅拷贝 lst1[1][0] = 20 # 修改嵌套列表的元素 print(lst) # 原列表受影响 → [1, [20, 3], 4] -
深拷贝(
copy.deepcopy()):完全复制所有层级的元素,内外层容器相互独立(修改拷贝后列表不影响原列表)。import copy lst = [1, [2, 3], 4] lst3 = copy.deepcopy(lst) lst3[1][0] = 200 # 修改嵌套列表 print(lst) # 原列表不受影响 → [1, [2, 3], 4]
3. 列表推导式进阶
-
嵌套列表转一维:通过嵌套推导式展平列表
nested_lst = [[1,2], [3,4], [5,6]] flat_lst = [x for sub_lst in nested_lst for x in sub_lst] # 结果:[1,2,3,4,5,6] -
多条件筛选:支持复杂条件组合
lst = [1,2,3,4,5,6] filtered_lst = [x for x in lst if x % 2 == 0 and x > 3] # 结果:[4,6]
4. 其他常用操作
enumerate()遍历 :同时获取索引和元素
lst = ["a", "b", "c"]
for idx, elem in enumerate(lst, start=1): # start指定索引起始值(默认0)
print(f"索引{idx}:{elem}") # 输出:索引1:a;索引2:b;索引3:c
-
len(lst):获取列表长度(元素个数)。 -
in/not in:判断元素是否在列表中(时间复杂度O(n))。lst = [1, 2, 3] print(2 in lst) # True print(4 not in lst) # True -
max(lst)/min(lst):获取列表中的最大值/最小值(元素需可比较)。 -
sum(lst):计算列表中所有数字元素的和(非数字元素会报错)。
(三)注意事项与常见坑
- 可变对象陷阱:列表是可变对象,修改操作(
append/pop/sort等)会直接改变原列表。 - 浅拷贝风险:嵌套列表的浅拷贝会导致内层元素共享,需谨慎使用
copy()或切片。 - 查找效率:列表查找元素(
in/index)的时间复杂度为O(n),数据量较大时建议改用字典/集合。 - 排序限制:
sort()要求元素类型一致,不同类型间无法比较排序。
二、元组(tuple):有序不可变的"只读容器"
元组与列表类似,有序、允许重复元素,但核心特性是不可变(创建后无法修改元素、添加/删除元素),底层基于数组实现,适用于存储无需修改的数据(如配置参数、坐标等),性能略优于列表。
(一)核心操作:增删改查(受不可变性限制)
1. 增:添加元素(无直接方式,仅间接拼接)
元组不可变,无法通过append/insert等方法直接添加元素,仅能通过拼接(+) 或拆包生成新元组(原元组不变),时间复杂度O(n+k)。
tup1 = (1, 2, 3)
tup2 = tup1 + (4, 5) # 拼接生成新元组,结果:(1, 2, 3, 4, 5)(tup1仍为(1,2,3))
tup3 = tup1 * 2 # 重复拼接,结果:(1, 2, 3, 1, 2, 3)
# 单元素元组需加逗号(否则会被解析为普通数据类型)
tup4 = (6,) # 正确的单元素元组
tup5 = (6) # 错误,实际是整数6
2. 删:删除元素(无直接方式,仅能删除整个元组)
元组不可变,无法删除单个元素,仅能通过del语句删除整个元组(后续无法访问)。
tup = (1, 2, 3)
# del tup[0] # 报错:TypeError: 'tuple' object doesn't support item deletion(无法删除单个元素)
del tup # 正确,删除整个元组(后续访问tup会报错)
3. 改:修改元素(无直接方式,仅间接替换)
元组不可变,无法直接修改元素值,但如果元组中包含可变容器(如列表),可修改该可变容器的内部元素(元组本身的结构未变)。
tup = (1, [2, 3], 4)
# tup[0] = 10 # 报错:TypeError: 'tuple' object does not support item assignment(无法修改元组元素)
tup[1][0] = 20 # 允许修改元组中的列表元素,结果:(1, [20, 3], 4)
4. 查:查找元素(与列表完全一致,支持索引/切片/index/count)
元组的查找操作与列表完全相同,因有序性,可通过索引快速访问元素,时间复杂度与列表一致。
tup = (1, 2, 3, 2, 4)
print(tup[2]) # 索引查找 → 3
print(tup[1:4]) # 切片查找 → (2, 3, 2)
print(tup.index(2)) # 查找第一个2的索引 → 1
print(tup.count(2)) # 统计2出现的次数 → 2
print(3 in tup) # 判断元素是否存在 → True
(二)常用进阶操作
1. 元组拆包(核心特性)
将元组的元素批量赋值给变量,支持"部分拆包"和"忽略元素"(用_表示),是元组最常用的进阶用法。
tup = (10, 20, 30)
a, b, c = tup # 完全拆包 → a=10, b=20, c=30
# 部分拆包(用*接收剩余元素)
tup = (1, 2, 3, 4, 5)
x, y, *rest = tup # x=1, y=2, rest=[3,4,5](rest为列表)
x, *middle, z = tup # x=1, middle=[2,3,4], z=5
# 忽略元素
tup = (100, 200, 300)
_, b, _ = tup # 仅获取第二个元素 → b=200
2. 命名元组(namedtuple)
解决普通元组"索引语义不清晰"的问题,提供更具可读性的属性访问方式。
from collections import namedtuple
# 定义命名元组类型
Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
print(p.x) # 输出:1(通过属性访问,比索引更清晰)
print(p.y) # 输出:2
3. 元组与其他容器的转换
-
元组转列表:
list(tup),利用列表的可变性扩展操作(如添加/删除元素)。tup = (1, 2, 3) lst = list(tup) # 结果:[1, 2, 3](可后续执行lst.append(4)) -
列表转元组:
tuple(lst),将可变列表转为不可变元组(如保护数据不被修改)。lst = [1, 2, 3] tup = tuple(lst) # 结果:(1, 2, 3)(后续无法修改元素) -
元组转集合:
set(tup),自动去重(集合不允许重复元素),但会丢失有序性。tup = (1, 2, 2, 3) s = set(tup) # 结果:{1, 2, 3}(无序、去重) -
字符串转元组:
tuple(str),按字符拆分生成元组。s = "hello" tup = tuple(s) # 结果:('h', 'e', 'l', 'l', 'o')
4. 元组作为字典键
元组的不可变性使其可作为字典的键,常用于存储坐标等复合数据。
# 使用元组作为键存储坐标对应的值
coord_dict = {(1,2): "A点", (3,4): "B点"}
print(coord_dict[(1,2)]) # 输出:A点
5. 其他常用操作
len(tup):获取元组长度(元素个数)。max(tup)/min(tup):获取元组中的最大值/最小值(元素需可比较)。sum(tup):计算元组中所有数字元素的和(非数字元素会报错)。- 嵌套元组操作:元组支持多层嵌套,查找时通过多索引访问内层元素。
nested_tup = (1, (2, 3), (4, (5, 6))) print(nested_tup[1][0]) # 访问第二层第一个元素 → 2 print(nested_tup[2][1][1]) # 访问第三层第二个元素 → 6
(三)注意事项与常见坑
- 不可变性本质:元组的"不可变"是指元素的引用不可变,而非元素本身不可变。若元素是可变对象(如列表),其内部内容可修改,但元组的长度和元素引用无法改变。
- 单元素元组:必须在元素后加逗号(
(x,)),否则会被解析为普通数据类型。 - 性能优势:元组的性能优于列表(创建速度更快、占用内存更少),适合存储静态数据(如配置项、函数返回的多值结果)。
- 作为字典键:元组可作为字典的键(因不可变),而列表不可(因可变)。
三、字典(dict):有序可变的"键值对映射"
字典是Python中最核心的映射型容器,有序(Python 3.7+正式保证插入顺序)、可变、键唯一且不可变,底层基于哈希表实现,适用于通过"键"快速查找数据(时间复杂度O(1)),是处理关联数据的首选。
(一)核心操作:增删改查
1. 增:添加键值对(5种常用方式)
-
直接键赋值(
dict[key] = value):最常用方式,若键不存在则添加,若键已存在则覆盖原有值。dic = {"name": "小明", "age": 18} dic["gender"] = "男" # 键不存在,添加新键值对 → {"name": "小明", "age": 18, "gender": "男"} dic["age"] = 19 # 键已存在,覆盖值 → {"name": "小明", "age": 19, "gender": "男"} -
update(other):批量添加/更新键值对,other可为字典、键值对序列(如列表)或关键字参数。
dic = {"name": "小明"}
# 传入字典
dic.update({"age": 18, "gender": "男"}) # 结果:{"name": "小明", "age": 18, "gender": "男"}
# 传入键值对列表
dic.update([("score", 90), ("grade", "高三")]) # 结果:{"name": "小明", "age": 18, "gender": "男", "score": 90, "grade": "高三"}
# 传入关键字参数(键需为合法标识符)
dic.update(city="北京", school="北京大学") # 结果:新增city和school键
-
Python 3.9+ 字典合并运算符
|:更简洁的字典合并方式dic1 = {"a":1, "b":2} dic2 = {"b":3, "c":4} merged_dic = dic1 | dic2 # 合并,相同键取后者值 → {"a":1, "b":3, "c":4} dic1 |= dic2 # 原地更新 → dic1变为{"a":1, "b":3, "c":4} -
setdefault(key, default=None):若键不存在,添加键值对(key: default)并返回default;若键已存在,返回原有值(不修改)。
dic = {"name": "小明"}
res1 = dic.setdefault("age", 18) # 键不存在,添加并返回18 → dic变为{"name": "小明", "age": 18}
res2 = dic.setdefault("age", 20) # 键已存在,返回原有值18 → dic不变
-
字典推导式:通过条件筛选或映射生成新字典,适合批量创建符合规则的键值对。
# 生成"数字:平方"的字典 square_dic = {x: x*x for x in range(1, 6)} # 结果:{1:1, 2:4, 3:9, 4:16, 5:25} # 过滤键值对(保留偶数键) origin_dic = {1:1, 2:4, 3:9, 4:16} even_dic = {k: v for k, v in origin_dic.items() if k % 2 == 0} # 结果:{2:4, 4:16} -
fromkeys(seq, value=None):创建新字典,以序列seq中的元素为键,所有键的默认值为value(默认None)。注意:若value是可变对象,所有键会共享同一个对象。
keys = ["name", "age", "gender"]
dic = dict.fromkeys(keys) # 结果:{"name": None, "age": None, "gender": None}
dic2 = dict.fromkeys(keys, "未知") # 结果:{"name": "未知", "age": "未知", "gender": "未知"}
# 警告:可变值共享问题
dic = dict.fromkeys(["a", "b"], [])
dic["a"].append(1) # 结果:{"a": [1], "b": [1]}(b的列表也被修改)
# 解决方案:用字典推导式创建独立对象
dic = {k: [] for k in ["a", "b"]} # 每个键对应独立列表
dic["a"].append(1) # 结果:{"a": [1], "b": []}(不影响b)
2. 删:删除键值对(5种常用方式)
pop(key, default=None):删除指定键key,返回对应的值;若键不存在,返回default(无default则报错),时间复杂度O(1)。
dic = {"name": "小明", "age": 18}
res = dic.pop("age") # 删除"age"键,返回18 → dic变为{"name": "小明"}
res2 = dic.pop("gender", "未知") # 键不存在,返回默认值"未知" → dic不变
# dic.pop("score") # 键不存在且无默认值,报错:KeyError: 'score'
popitem():删除并返回字典中的"最后一个键值对"(Python 3.7+后有序),返回形式为元组(key, value),时间复杂度O(1);空字典调用会报错。
dic = {"name": "小明", "age": 18, "gender": "男"}
res = dic.popitem() # 删除最后一个键值对,返回("gender", "男") → dic变为{"name": "小明", "age": 18}
del语句 :删除指定键值对或整个字典,无返回值。
dic = {"name": "小明", "age": 18}
del dic["name"] # 删除"name"键 → dic变为{"age": 18}
del dic # 删除整个字典(后续无法访问)
clear():清空字典中所有键值对,保留字典对象(仅删除内容),时间复杂度O(n)。
dic = {"name": "小明", "age": 18}
dic.clear() # 结果:{}(字典仍存在,内容为空)
- 字典推导式(过滤删除):通过条件筛选保留键值对,间接实现"删除特定键值对"。
dic = {"name": "小明", "age": 18, "gender": "男", "score": 90} # 保留键不是"gender"的键值对(间接删除"gender") dic = {k: v for k, v in dic.items() if k != "gender"} # 结果:{"name": "小明", "age": 18, "score": 90}
3. 改:修改键值对(3种核心方式)
-
直接键赋值:
dict[key] = 新值,最直接的修改方式(键必须已存在)。dic = {"name": "小明", "age": 18} dic["age"] = 19 # 修改"age"的值 → {"name": "小明", "age": 19} -
update(other):批量修改已有键的值(传入的键存在则更新,不存在则添加)。
dic = {"name": "小明", "age": 18, "gender": "男"}
dic.update({"age": 19, "gender": "女"}) # 批量修改 → {"name": "小明", "age": 19, "gender": "女"}
- 修改键名(间接修改):字典的键不可直接修改,需先删除原键,再添加新键(值不变)。
dic = {"name": "小明", "age": 18} # 修改"name"为"full_name" dic["full_name"] = dic.pop("name") # 先删除"name"并获取值,再添加新键 → {"age": 18, "full_name": "小明"}
4. 查:查找数据(6种常用方式)
-
直接键访问(
dict[key]):通过键直接获取值,时间复杂度O(1);键不存在则报错。dic = {"name": "小明", "age": 18} print(dic["name"]) # 输出:小明 # print(dic["gender"]) # 键不存在,报错:KeyError: 'gender' -
get(key, default=None):通过键获取值,键不存在则返回default(默认None),不报错,是最安全的查找方式。若需判断值是否存在,需用in dic.values(),时间复杂度O(n)。
dic = {"name": "小明", "age": 18}
print(dic.get("name")) # 输出:小明
print(dic.get("gender", "未知")) # 键不存在,返回默认值 → 未知
# 判断值是否存在
print(18 in dic.values()) # True(需遍历,O(n))
items():返回字典中所有键值对的视图对象(dict_items),支持迭代(遍历键和值)。
dic = {"name": "小明", "age": 18}
for k, v in dic.items():
print(f"{k}: {v}") # 输出:name: 小明;age: 18
keys():返回字典中所有键的视图对象(dict_keys),支持迭代(遍历键)。
for k in dic.keys():
print(k) # 输出:name;age
values():返回字典中所有值的视图对象(dict_values),支持迭代(遍历值,允许重复)。
dic = {"name": "小明", "age": 18, "score": 90}
for v in dic.values():
print(v) # 输出:小明;18;90
in/not in:判断键是否存在于字典中(仅判断键,不判断值),时间复杂度O(1)。
dic = {"name": "小明", "age": 18}
print("name" in dic) # True(键存在)
print("gender" not in dic) # True(键不存在)
print(18 in dic) # False(仅判断键,不判断值)
(二)常用进阶操作
1. 字典遍历(4种常用方式)
-
遍历键(默认):
for k in dic等价于for k in dic.keys()。dic = {"name": "小明", "age": 18} for k in dic: print(k, dic[k]) # 输出:name 小明;age 18 -
遍历键值对(
items()):最常用,直接获取键和值。for k, v in dic.items(): print(f"{k} = {v}") # 输出:name = 小明;age = 18 -
遍历值(
values()):仅获取值(不关心键)。for v in dic.values(): print(v) # 输出:小明;18 -
遍历有序字典(Python 3.7+):默认按插入顺序遍历,若需自定义顺序,可结合
sorted()。dic = {"b": 2, "a": 1, "c": 3} # 按键升序遍历 for k in sorted(dic.keys()): print(k, dic[k]) # 输出:a 1;b 2;c 3 # 按值降序遍历(返回列表) sorted_by_val = sorted(dic.items(), key=lambda x: x[1], reverse=True) print(sorted_by_val) # 输出:[('c', 3), ('b', 2), ('a', 1)]
2. 字典复制(浅拷贝与深拷贝)
-
浅拷贝(
copy()/dict(dic)/{** dic}):复制字典外层键值对,若值是可变容器(如列表、字典),则仍为引用(修改内层会影响原字典)。dic = {"name": "小明", "hobbies": ["读书", "运动"]} dic1 = dic.copy() # 浅拷贝 dic2 = dict(dic) # 浅拷贝 dic3 = {** dic} # 浅拷贝(字典解包) dic1["hobbies"].append("旅行") # 修改嵌套列表 print(dic) # 原字典受影响 → {"name": "小明", "hobbies": ["读书", "运动", "旅行"]} -
深拷贝(
copy.deepcopy()):完全复制所有层级的键值对,内外层容器相互独立(修改拷贝后字典不影响原字典)。import copy dic = {"name": "小明", "hobbies": ["读书", "运动"], "info": {"age": 18}} dic4 = copy.deepcopy(dic) dic4["hobbies"].append("旅行") dic4["info"]["age"] = 19 print(dic) # 原字典不受影响 → {"name": "小明", "hobbies": ["读书", "运动"], "info": {"age": 18}}
3. 嵌套字典操作
字典支持多层嵌套(字典中包含字典),操作时通过"多键索引"或递归实现增删改查。
# 定义嵌套字典
nested_dic = {
"student": {
"name": "小明",
"age": 18,
"score": {"math": 90, "english": 85}
},
"teacher": {"name": "李老师", "subject": "数学"}
}
# 查:获取嵌套值
print(nested_dic["student"]["score"]["math"]) # 输出:90
# 改:修改嵌套值
nested_dic["student"]["score"]["english"] = 95 # 结果:english分数改为95
# 增:添加嵌套键值对
nested_dic["student"]["gender"] = "男" # 给student添加gender键
# 删:删除嵌套键值对
del nested_dic["teacher"]["subject"] # 删除teacher的subject键
4. 缺省字典(defaultdict)
解决"键不存在时避免KeyError"的进阶工具,自动为不存在的键初始化默认值。
from collections import defaultdict
# 默认为列表类型
dic = defaultdict(list)
dic["hobbies"].append("读书") # 无需先初始化列表,直接添加元素
print(dic) # 输出:defaultdict(<class 'list'>, {'hobbies': ['读书']})
# 默认为整型(可用于计数)
count_dic = defaultdict(int)
count_dic["apple"] += 1 # 自动初始化为0再加1
print(count_dic["apple"]) # 输出:1
5. 其他常用操作
len(dic):获取字典中键值对的个数(长度)。
dic = {"name": "小明", "age": 18}
print(len(dic)) # 输出:2
sorted(dic, key=None, reverse=False):对字典的键进行排序,返回排序后的键列表(字典本身无序,仅返回排序后的键)。
dic = {"b": 2, "a": 1, "c": 3}
sorted_keys = sorted(dic) # 按键升序 → ["a", "b", "c"]
sorted_keys_rev = sorted(dic, reverse=True) # 按键降序 → ["c", "b", "a"]
# 按值排序(通过key参数指定)
sorted_keys_by_val = sorted(dic, key=lambda k: dic[k]) # 按值升序 → ["a", "b", "c"]
-
字典解包(
**):将字典的键值对解包为关键字参数,常用于函数调用或字典拼接。# 字典拼接(Python 3.5+支持) dic1 = {"name": "小明", "age": 18} dic2 = {"gender": "男", "score": 90} merged_dic = {** dic1, ** dic2} # 结果:{"name": "小明", "age": 18, "gender": "男", "score": 90} # 函数调用解包 def print_info(name, age, gender): print(f"{name}, {age}岁, {gender}") print_info(** dic1, gender="男") # 等价于print_info(name="小明", age=18, gender="男") -
判断字典是否为空:通过
if not dic判断(空字典为False,非空为True)。empty_dic = {} print(not empty_dic) # 输出:True(空字典) dic = {"name": "小明"} print(not dic) # 输出:False(非空字典)
(三)注意事项与常见坑
-
键的类型限制:字典的键必须是不可变类型(如数字、字符串、元组),可变类型(列表、字典、集合)不能作为键(会报错
TypeError: unhashable type)。dic = {[1,2]: "错误"} # 报错:TypeError: unhashable type: 'list' dic = {(1,2): "正确"} # 元组为不可变类型,允许作为键 -
值的类型:字典的值可以是任意类型(包括可变类型),且允许重复。
-
有序性说明:Python 3.7+后,字典会保留键值对的插入顺序(底层优化),但仍不支持通过索引直接访问(需通过键访问)。
-
视图对象特性:字典的视图对象(
items()/keys()/values())是"动态关联"的——原字典修改后,视图对象会同步更新。dic = {"name": "小明", "age": 18} items = dic.items() dic["gender"] = "男" # 修改原字典 print(items) # 输出:dict_items([('name', '小明'), ('age', '18'), ('gender', '男')])(视图同步更新) -
fromkeys可变值陷阱 :使用可变对象作为fromkeys的默认值会导致所有键共享同一对象。
四、集合(set):无序可变的"唯一元素集合"
集合是Python中用于存储"唯一元素"的容器,无序、可变、不允许重复元素,底层基于哈希表实现,核心优势是"快速去重"和"集合运算"(如交集、并集),时间复杂度多为O(1)。
(一)核心操作:增删改查
1. 增:添加元素(3种常用方式)
add(x):添加单个元素x到集合中,若元素已存在则不报错(忽略重复),时间复杂度O(1)。
s = {1, 2, 3}
s.add(4) # 添加新元素 → {1, 2, 3, 4}
s.add(2) # 元素已存在,无操作 → {1, 2, 3, 4}
s.add("hello") # 支持不同类型元素 → {1, 2, 3, 4, 'hello'}
update(iterable):批量添加可迭代对象(如列表、元组、字符串、集合)的所有元素,自动去重,时间复杂度O(k)(k为可迭代对象长度)。
s = {1, 2, 3}
s.update([3, 4, 5]) # 批量添加列表(去重)→ {1, 2, 3, 4, 5}
s.update(("a", "b"), "cd") # 批量添加元组和字符串 → {1, 2, 3, 4, 5, 'a', 'b', 'c', 'd'}
s.update({6, 7}) # 批量添加集合 → {1, 2, 3, 4, 5, 'a', 'b', 'c', 'd', 6, 7}
- 集合推导式:通过条件筛选或映射生成新集合,自动去重,适合批量创建符合规则的唯一元素集合。
# 生成1-10的偶数集合 even_set = {x for x in range(1, 11) if x % 2 == 0} # 结果:{2, 4, 6, 8, 10} # 去重字符串中的字符 str_set = {c for c in "hello world"} # 结果:{'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'}(自动去重'l')
2. 删:删除元素(4种常用方式)
remove(x):删除集合中的元素x,元素不存在则报错(KeyError),时间复杂度O(1)。
s = {1, 2, 3, 4}
s.remove(3) # 删除元素3 → {1, 2, 4}
# s.remove(5) # 元素不存在,报错:KeyError: 5
discard(x):删除集合中的元素x,元素不存在则不报错(忽略操作),时间复杂度O(1)(推荐优先使用)。
s = {1, 2, 3, 4}
s.discard(3) # 删除元素3 → {1, 2, 4}
s.discard(5) # 元素不存在,无操作 → {1, 2, 4}(不报错)
pop():随机删除并返回集合中的一个元素(因集合无序,无法指定删除位置),时间复杂度O(1);空集合调用会报错。
s = {1, 2, 3, 4}
res = s.pop() # 随机删除一个元素(如1),返回1 → s变为{2, 3, 4}
clear():清空集合中所有元素,保留集合对象(仅删除内容),时间复杂度O(n)。
s = {1, 2, 3}
s.clear() # 结果:set()(空集合)
3. 改:修改元素(无直接方式,需间接实现)
集合是"无序且元素唯一"的容器,无直接修改元素的方法(无法通过索引或键定位元素),若需修改元素,需先删除原元素,再添加新元素。
s = {1, 2, 3, 4}
# 要将2改为20:先删除2,再添加20
s.discard(2)
s.add(20) # 结果:{1, 3, 4, 20}
4. 查:查找元素(2种核心方式)
in/not in:判断元素是否在集合中,时间复杂度O(1)(远快于列表的O(n)),是集合最常用的查找方式。
s = {1, 2, 3, 4}
print(2 in s) # True(元素存在)
print(5 not in s) # True(元素不存在)
- 遍历查找:通过
for循环遍历集合中的所有元素(无序),适合批量处理元素。s = {1, 2, 3, 4} for elem in s: print(elem) # 输出顺序不确定(如:3,1,4,2)
(二)核心优势:集合运算(交集、并集、差集等)
集合的核心价值在于"集合运算",支持数学中的交集、并集、差集、对称差集等操作,语法简洁且效率高。
1. 交集(& 或 intersection()):返回两个集合的共同元素
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# 方式1:运算符&
inter1 = s1 & s2 # 结果:{3, 4}
# 方式2:方法intersection()
inter2 = s1.intersection(s2) # 结果:{3, 4}
# 支持多个集合交集
s3 = {4, 5, 6, 7}
inter3 = s1 & s2 & s3 # 结果:{4}
2. 并集(| 或 union()):返回两个集合的所有元素(自动去重)
s1 = {1, 2, 3}
s2 = {3, 4, 5}
# 方式1:运算符|
union1 = s1 | s2 # 结果:{1, 2, 3, 4, 5}
# 方式2:方法union()
union2 = s1.union(s2) # 结果:{1, 2, 3, 4, 5}
# 支持多个集合并集
s3 = {5, 6, 7}
union3 = s1 | s2 | s3 # 结果:{1, 2, 3, 4, 5, 6, 7}
3. 差集(- 或 difference()):返回"仅在第一个集合中存在"的元素
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# 方式1:运算符-
diff1 = s1 - s2 # 仅s1有,s2没有 → {1, 2}
diff2 = s2 - s1 # 仅s2有,s1没有 → {5, 6}
# 方式2:方法difference()
diff3 = s1.difference(s2) # 结果:{1, 2}
4. 对称差集(^ 或 symmetric_difference()):返回"仅在一个集合中存在"的元素(交集的补集)
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# 方式1:运算符^
sym_diff1 = s1 ^ s2 # 仅s1或仅s2有 → {1, 2, 5, 6}
# 方式2:方法symmetric_difference()
sym_diff2 = s1.symmetric_difference(s2) # 结果:{1, 2, 5, 6}
5. 子集与超集判断(<=/>= 或 issubset()/issuperset())
- 子集:若集合A的所有元素都在集合B中,则A是B的子集。
- 超集:若集合B的所有元素都在集合A中,则A是B的超集。
s1 = {1, 2, 3}
s2 = {1, 2, 3, 4, 5}
# 子集判断
print(s1 <= s2) # True(s1是s2的子集)
print(s1.issubset(s2)) # True
# 超集判断
print(s2 >= s1) # True(s2是s1的超集)
print(s2.issuperset(s1)) # True
# 真子集(A是B的子集且A≠B)
print(s1 < s2) # True
# 真超集(B是A的超集且B≠A)
print(s2 > s1) # True
6. 集合运算的"原地修改"方法(带update后缀)
上述运算默认返回新集合(原集合不变),若需直接修改原集合,可使用带update后缀的方法:
s1 = {1, 2, 3}
s2 = {3, 4, 5}
s1.intersection_update(s2) # 原地求交集,s1变为{3}
s1.update(s2) # 原地求并集,s1变为{3, 4, 5}
s1.difference_update(s2) # 原地求差集,s1变为{3}
(三)常用进阶操作
1. 集合与其他容器的转换
-
集合转列表:
list(s),将集合转为列表(无序,顺序不确定)。s = {1, 2, 3} lst = list(s) # 结果可能是[1, 2, 3]或[2, 1, 3]等(顺序不确定) -
列表转集合:
set(lst),自动去重(核心用途)。lst = [1, 2, 2, 3, 3, 3] s = set(lst) # 结果:{1, 2, 3}(去重,O(n)效率) -
集合转元组:
tuple(s),将集合转为元组(无序)。s = {1, 2, 3} tup = tuple(s) # 结果顺序不确定 -
字符串转集合:
set(str),按字符拆分并去重。s_str = "hello world" s = set(s_str) # 结果:{'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'}(去重'l')
2. 不可变集合(frozenset)
frozenset是不可变的集合,可作为字典键或集合元素(普通集合的元素必须不可变)。
fs = frozenset({1, 2, 3})
# 作为字典键
dic = {fs: "不可变集合"}
# 作为集合元素
s = {fs, 4, 5}
print(dic[fs]) # 输出:不可变集合
3. 集合运算的实际场景
-
用户标签交集判断:快速找出共同兴趣
# 场景:两个用户标签的共同兴趣 user1_tags = {"篮球", "音乐", "电影"} user2_tags = {"音乐", "游戏", "电影"} common_tags = user1_tags & user2_tags # 结果:{"音乐", "电影"} -
列表去重效率对比:集合去重远快于列表推导式
lst = [1,2,2,3,3,3] # 集合去重(O(n)) unique_lst1 = list(set(lst)) # 高效 # 列表推导式去重(O(n²),效率低) unique_lst2 = [] [unique_lst2.append(x) for x in lst if x not in unique_lst2]
4. 其他常用操作
len(s):获取集合中元素的个数(长度)。max(s)/min(s):获取集合中的最大值/最小值(元素需可比较)。sum(s):计算集合中所有数字元素的和(非数字元素会报错)。- 空集合判断:通过
if not s判断(空集合为False,非空为True),注意空集合的表示是set(),而非{}({}是空字典)。empty_s = set() print(not empty_s) # 输出:True(空集合) s = {1, 2} print(not s) # 输出:False(非空集合)
(四)注意事项与常见坑
-
元素类型限制:集合的元素必须是不可变类型(如数字、字符串、元组),可变类型(列表、字典、集合)不能作为集合元素(会报错
TypeError: unhashable type)。s = {[1,2]} # 报错:TypeError: unhashable type: 'list' s = {(1,2)} # 元组为不可变类型,允许作为元素 -
无序性:集合是无序的,无法通过索引或键访问元素,仅能通过
in判断存在性或遍历访问。转换后会丢失有序性。 -
去重原理:集合的去重是基于元素的哈希值,因此只有可哈希(不可变)的元素才能被去重,这也是集合元素必须为不可变类型的原因。
-
空集合表示:空集合必须用
set()表示,{}是空字典。
五、四大容器的特性与适用场景对比
为了更清晰地选择合适的容器,我们将四大容器的核心特性、操作效率、适用场景总结为表格:
| 容器类型 | 核心特性 | 增删改查效率(平均) | 适用场景 | 是否可哈希 |
|---|---|---|---|---|
| 列表(list) | 有序、可变、允许重复元素 | 增(末尾O(1),中间O(n));查(索引O(1),inO(n)) |
存储有序且需频繁修改的数据(如动态列表) | 否(可变) |
| 元组(tuple) | 有序、不可变、允许重复元素 | 增删改受限;查(索引O(1)) | 存储静态数据(如配置项、函数返回多值) | 是(不可变) |
| 字典(dict) | 有序(3.7+)、可变、键唯一不可变 | 增删改查均O(1)(基于哈希) | 存储关联数据(如键值对映射、快速查找) | 否(可变) |
| 集合(set) | 无序、可变、元素唯一不可变 | 增删查O(1);集合运算O(k) | 数据去重、集合关系判断(如交集/并集) | 否(可变) |
补充说明:
- 可哈希性:只有不可变类型(元组、字符串、数字等)可哈希,可作为字典键或集合元素
- frozenset:作为不可变集合,是可哈希的,可用于需要集合特性但要求不可变的场景
六、容器通用特性与迭代器
1. 容器的可迭代性
四大容器均为可迭代对象,可使用iter()获取迭代器,next()逐个访问元素。
lst = [1, 2, 3]
it = iter(lst) # 获取迭代器
print(next(it)) # 输出:1
print(next(it)) # 输出:2
2. 内存占用对比
- 元组 vs 列表:元组占用内存更少(约少10-20%),创建速度更快
- 字典/集合:因哈希表实现,有额外内存开销(空间换时间)
- 内存敏感场景:优先使用元组存储静态数据,避免不必要的字典/集合
七、总结
Python四大容器各有其独特的定位:
- 列表是"动态有序数组",灵活但查找效率较低(O(n));
- 元组是"只读有序容器",轻量且安全,适合存储静态数据;
- 字典是"哈希映射表",是关联数据的最佳选择,操作效率极高(O(1));
- 集合是"哈希集合",核心优势是去重与集合运算(O(1)或O(k))。
在实际开发中,需根据数据的"有序性、可变性、唯一性"需求,结合操作效率选择合适的容器——例如:
- 若需要"有序且可修改的列表",选列表;
- 若需要"键值对快速查找",选字典;
- 若需要"去重或判断元素关系",选集合;
- 若需要"保护数据不被修改",选元组。
掌握四大容器的增删改查与进阶操作,是Python数据处理的基础,也是编写高效代码的关键。对于进阶开发者,建议熟练掌握namedtuple、defaultdict、frozenset等工具类,以及Python 3.9+的新特性(字典合并运算符),以应对更复杂的实际场景。

浙公网安备 33010602011771号