第4章 容器类型基础
4.1 字符串常用操作方法
字符串是Python中最常用的容器类型之一,它用于存储和处理文本数据。在Python中,字符串是不可变的序列类型,这意味着一旦创建就不能直接修改。
字符串基本操作方法
| 功能名称 | 实例调用方法 | 具体功能、注意事项、必需参数/可选参数 |
|---|---|---|
| 字符串连接 | str1 + str2 | 连接两个字符串,返回新字符串 |
| 字符串重复 | str * n | 将字符串重复n次,n必须是非负整数 |
| 字符串长度 | len(str) | 返回字符串中字符的数量 |
| 字符查找 | str.find(sub) | 查找子字符串sub首次出现的位置,未找到返回-1 |
| 字符串分割 | str.split(sep) | 按分隔符sep分割字符串,返回列表,sep默认为空白字符 |
# 字符串连接示例
str1 = "Hello"
str2 = "World"
result = str1 + " " + str2 # 连接两个字符串,中间加空格
print(f"连接结果: {result}") # 输出: Hello World
# 字符串重复示例
repeat_str = "Python" * 3 # 将"Python"重复3次
print(f"重复结果: {repeat_str}") # 输出: PythonPythonPython
# 字符串长度示例
text = "Python编程"
length = len(text) # 计算字符串长度,中文字符也计为1个字符
print(f"字符串长度: {length}") # 输出: 8
# 字符查找示例
sentence = "Learning Python is fun"
position = sentence.find("Python") # 查找"Python"在字符串中的位置
if position != -1:
print(f"'Python'在位置 {position} 处找到") # 输出: 'Python'在位置 9 处找到
else:
print("'Python'未找到")
# 字符串分割示例
data = "apple,banana,orange"
fruits = data.split(",") # 按逗号分割字符串
print(f"分割结果: {fruits}") # 输出: ['apple', 'banana', 'orange']字符串作为不可变序列,提供了丰富的操作方法来处理文本数据,这些基本操作是日常编程中处理文本的基础。
4.2 列表的创建与基本操作
列表是Python中最灵活的容器类型,它可以存储任意类型的对象,并且是可变的,这意味着我们可以随时修改列表的内容。
列表基本操作方法
| 功能名称 | 实例调用方法 | 具体功能、注意事项、必需参数/可选参数 |
|---|---|---|
| 列表创建 | [item1, item2, ...] 或 list() | 创建包含指定元素的列表或空列表 |
| 添加元素 | list.append(item) | 在列表末尾添加单个元素 |
| 插入元素 | list.insert(index, item) | 在指定位置插入元素 |
| 删除元素 | list.remove(item) | 删除第一个匹配的元素 |
| 列表长度 | len(list) | 返回列表中元素的数量 |
# 列表创建示例
empty_list = [] # 创建空列表
numbers = [1, 2, 3, 4, 5] # 创建包含数字的列表
mixed_list = [1, "hello", 3.14, True] # 创建包含不同类型元素的列表
print(f"空列表: {empty_list}")
print(f"数字列表: {numbers}")
print(f"混合列表: {mixed_list}")
# 添加元素示例
fruits = ["apple", "banana"]
fruits.append("orange") # 在列表末尾添加"orange"
print(f"添加元素后: {fruits}") # 输出: ['apple', 'banana', 'orange']
# 插入元素示例
fruits.insert(1, "grape") # 在索引1处插入"grape"
print(f"插入元素后: {fruits}") # 输出: ['apple', 'grape', 'banana', 'orange']
# 删除元素示例
try:
fruits.remove("banana") # 删除"banana"
print(f"删除元素后: {fruits}") # 输出: ['apple', 'grape', 'orange']
except ValueError:
print("要删除的元素不存在")
# 列表长度示例
length = len(fruits) # 获取列表长度
print(f"列表长度: {length}") # 输出: 3列表作为可变序列,提供了动态管理数据的能力,是Python编程中最常用的容器类型之一。
4.3 元组的不可变特性
元组是Python中的另一种序列类型,与列表类似,但元组是不可变的,一旦创建就不能修改其内容。这种不可变性使得元组在某些场景下比列表更安全、更高效。
元组基本操作方法
| 功能名称 | 实例调用方法 | 具体功能、注意事项、必需参数/可选参数 |
|---|---|---|
| 元组创建 | (item1, item2, ...) 或 tuple() | 创建包含指定元素的元组或空元组 |
| 元素访问 | tuple[index] | 通过索引访问元组中的元素 |
| 元组长度 | len(tuple) | 返回元组中元素的数量 |
| 元素计数 | tuple.count(item) | 统计指定元素在元组中出现的次数 |
| 元素索引 | tuple.index(item) | 返回指定元素在元组中首次出现的索引 |
# 元组创建示例
empty_tuple = () # 创建空元组
single_tuple = (42,) # 创建单元素元组,注意逗号不能省略
coordinates = (3, 4) # 创建包含两个元素的元组
mixed_tuple = (1, "hello", 3.14) # 创建包含不同类型元素的元组
print(f"空元组: {empty_tuple}")
print(f"单元素元组: {single_tuple}")
print(f"坐标元组: {coordinates}")
print(f"混合元组: {mixed_tuple}")
# 元素访问示例
x, y = coordinates # 元组解包
print(f"x坐标: {x}, y坐标: {y}") # 输出: x坐标: 3, y坐标: 4
# 元组长度示例
length = len(coordinates) # 获取元组长度
print(f"元组长度: {length}") # 输出: 2
# 元素计数示例
data = (1, 2, 2, 3, 2, 4)
count = data.count(2) # 统计数字2出现的次数
print(f"数字2出现次数: {count}") # 输出: 3
# 元素索引示例
try:
index = data.index(3) # 查找数字3的索引
print(f"数字3的索引: {index}") # 输出: 3
except ValueError:
print("要查找的元素不存在")
# 尝试修改元组(会引发错误)
try:
coordinates[0] = 5 # 这会引发TypeError
except TypeError as e:
print(f"错误: {e}") # 输出: 错误: 'tuple' object does not support item assignment元组的不可变特性使其在需要保证数据完整性的情况下非常有用,比如作为字典的键或函数的返回值。
4.4 字典的键值对存储
字典是Python中唯一的内置映射类型,它存储键值对(key-value pairs),允许通过键快速查找对应的值。字典是可变的,可以随时添加、修改或删除键值对。
字典基本操作方法
| 功能名称 | 实例调用方法 | 具体功能、注意事项、必需参数/可选参数 |
|---|---|---|
| 字典创建 | {key1: value1, key2: value2, ...} 或 dict() | 创建包含指定键值对的字典或空字典 |
| 添加/修改 | dict[key] = value | 添加新键值对或修改现有键的值 |
| 获取值 | dict[key] 或 dict.get(key) | 通过键获取值,get方法在键不存在时返回None而非抛出异常 |
| 删除键值对 | del dict[key] 或 dict.pop(key) | 删除指定键的键值对,pop方法返回被删除的值 |
| 获取所有键 | dict.keys() | 返回包含所有键的视图对象 |
# 字典创建示例
empty_dict = {} # 创建空字典
student = {"name": "张三", "age": 20, "grade": "A"} # 创建包含学生信息的字典
print(f"空字典: {empty_dict}")
print(f"学生信息: {student}")
# 添加/修改示例
student["city"] = "北京" # 添加新键值对
student["age"] = 21 # 修改现有键的值
print(f"更新后的学生信息: {student}")
# 获取值示例
name = student["name"] # 直接通过键获取值
print(f"学生姓名: {name}")
# 使用get方法获取值(更安全)
phone = student.get("phone") # 键"phone"不存在,返回None
email = student.get("email", "未提供") # 键"email"不存在,返回默认值"未提供"
print(f"电话: {phone}") # 输出: 电话: None
print(f"邮箱: {email}") # 输出: 邮箱: 未提供
# 删除键值对示例
try:
del student["grade"] # 删除"grade"键值对
print(f"删除grade后的学生信息: {student}")
except KeyError:
print("要删除的键不存在")
# 使用pop方法删除并获取值
city = student.pop("city", "未知") # 删除"city"并获取其值
print(f"城市: {city}") # 输出: 城市: 北京
print(f"最终学生信息: {student}")
# 获取所有键示例
keys = list(student.keys()) # 获取所有键并转换为列表
print(f"所有键: {keys}") # 输出: ['name', 'age']字典通过键值对的方式提供了高效的查找和存储机制,是处理关联数据的理想选择。
4.5 集合的去重与集合运算
集合是Python中用于存储唯一元素的无序容器。集合的主要特点是自动去重和高效的成员检测,同时还支持数学上的集合运算,如并集、交集、差集等。
集合基本操作方法
| 功能名称 | 实例调用方法 | 具体功能、注意事项、必需参数/可选参数 |
|---|---|---|
| 集合创建 | {item1, item2, ...} 或 set() | 创建包含指定元素的集合或空集合 |
| 添加元素 | set.add(item) | 向集合中添加单个元素 |
| 删除元素 | set.remove(item) 或 set.discard(item) | 删除指定元素,remove在元素不存在时抛出异常,discard则不会 |
| 集合运算 | `set1 | set2(并集),set1 & set2(交集),set1 - set2` (差集) |
| 子集检查 | set1.issubset(set2) 或 set1 <= set2 | 检查set1是否为set2的子集 |
# 集合创建示例
empty_set = set() # 创建空集合(注意不能用{},那会创建空字典)
numbers = {1, 2, 3, 4, 5} # 创建包含数字的集合
duplicate_numbers = [1, 2, 2, 3, 3, 4, 5] # 包含重复元素的列表
unique_numbers = set(duplicate_numbers) # 转换为集合自动去重
print(f"空集合: {empty_set}")
print(f"数字集合: {numbers}")
print(f"去重后的集合: {unique_numbers}") # 输出: {1, 2, 3, 4, 5}
# 添加元素示例
fruits = {"apple", "banana"}
fruits.add("orange") # 添加新元素
fruits.add("apple") # 尝试添加已存在的元素(不会重复添加)
print(f"添加元素后的集合: {fruits}") # 输出: {'apple', 'banana', 'orange'}
# 删除元素示例
fruits.discard("banana") # 删除"banana",如果不存在也不会报错
print(f"删除banana后的集合: {fruits}") # 输出: {'apple', 'orange'}
# 尝试用remove删除不存在的元素
try:
fruits.remove("grape") # 这会引发KeyError
except KeyError:
print("要删除的元素'grape'不存在")
# 集合运算示例
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
union = set1 | set2 # 并集
intersection = set1 & set2 # 交集
difference = set1 - set2 # 差集(set1中有但set2中没有的元素)
symmetric_diff = set1 ^ set2 # 对称差集(两个集合中不同时存在的元素)
print(f"并集: {union}") # 输出: {1, 2, 3, 4, 5, 6}
print(f"交集: {intersection}") # 输出: {3, 4}
print(f"差集: {difference}") # 输出: {1, 2}
print(f"对称差集: {symmetric_diff}") # 输出: {1, 2, 5, 6}
# 子集检查示例
subset = {1, 2}
is_subset = subset.issubset(set1) # 检查subset是否为set1的子集
print(f"{subset} 是 {set1} 的子集: {is_subset}") # 输出: True集合通过自动去重和高效的集合运算,为处理唯一元素和执行数学集合操作提供了便利。
4.6 深拷贝与浅拷贝问题
在 Python 中处理嵌套容器(比如列表中包含字典、字典中包含列表等)时,经常会遇到“复制”操作。但你可能没意识到:默认的复制行为其实是“浅拷贝”,它只复制了最外层容器,而内部元素仍然是共享的!这就容易导致“改一处,动全局”的诡异 bug。
那什么是深拷贝?简单说就是递归地把所有层级的对象都重新创建一份,彼此完全独立,互不影响。
浅拷贝 vs 深拷贝核心区别
| 功能名称 | 调用方法 | 具体说明 |
|---|---|---|
| 浅拷贝 | copy.copy(obj) 或 list.copy() / dict.copy() | 只复制顶层容器,内部嵌套对象仍是引用(共享) |
| 深拷贝 | copy.deepcopy(obj) | 递归复制所有层级的对象,完全独立 |
示例:浅拷贝的“坑”
# 导入 copy 模块,这是使用深/浅拷贝的前提
import copy
# 创建一个嵌套列表:外层是列表,内层也是列表
original = [[1, 2], [3, 4]]
# 使用浅拷贝:注意这里只复制了外层列表
shallow_copy = copy.copy(original)
# 修改浅拷贝中的内层元素
shallow_copy[0][0] = 999
# 打印原列表,你会发现也被改了!
print("原始列表:", original) # 输出: [[999, 2], [3, 4]]
print("浅拷贝列表:", shallow_copy) # 输出: [[999, 2], [3, 4]]为什么? 因为
shallow_copy和original虽然是两个不同的列表对象,但它们内部的子列表[1,2]和[3,4]是同一个对象!所以改了浅拷贝里的子列表,原始列表自然也跟着变了。
示例:深拷贝的“解药”
# 导入 copy 模块
import copy
# 同样的嵌套列表
original = [[1, 2], [3, 4]]
# 使用深拷贝:递归复制所有层级
deep_copy = copy.deepcopy(original)
# 修改深拷贝中的内层元素
deep_copy[0][0] = 999
# 打印原列表,这次它安然无恙!
print("原始列表:", original) # 输出: [[1, 2], [3, 4]]
print("深拷贝列表:", deep_copy) # 输出: [[999, 2], [3, 4]]为什么安全? 因为
deepcopy不仅复制了外层列表,还把里面的[1,2]和[3,4]也重新创建了一份新的列表对象。所以两者彻底独立,互不干扰。
注意事项
- 性能代价:深拷贝会递归遍历整个对象树,如果对象非常大或嵌套极深,性能开销会显著增加。不要滥用!
- 循环引用:如果对象内部存在循环引用(比如 A 包含 B,B 又包含 A),
deepcopy能正确处理,不会无限递归。 - 不可变对象无所谓:对于字符串、数字、元组(且内部元素也不可变)这类不可变对象,浅拷贝和深拷贝效果一样,因为根本没法改。
- 切片是浅拷贝:
new_list = old_list[:]这种切片操作也是浅拷贝,同样有上述问题!
这节讲清楚了浅拷贝和深拷贝的本质区别,并通过实例展示了它们在嵌套容器中的不同行为,能帮你避开数据意外被修改的大坑。