Skip to content

第4章 容器类型基础

4.1 字符串常用操作方法

字符串是Python中最常用的容器类型之一,它用于存储和处理文本数据。在Python中,字符串是不可变的序列类型,这意味着一旦创建就不能直接修改。

字符串基本操作方法

功能名称实例调用方法具体功能、注意事项、必需参数/可选参数
字符串连接str1 + str2连接两个字符串,返回新字符串
字符串重复str * n将字符串重复n次,n必须是非负整数
字符串长度len(str)返回字符串中字符的数量
字符查找str.find(sub)查找子字符串sub首次出现的位置,未找到返回-1
字符串分割str.split(sep)按分隔符sep分割字符串,返回列表,sep默认为空白字符
python
# 字符串连接示例
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)返回列表中元素的数量
python
# 列表创建示例
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)返回指定元素在元组中首次出现的索引
python
# 元组创建示例
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()返回包含所有键的视图对象
python
# 字典创建示例
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则不会
集合运算`set1set2(并集),set1 & set2(交集),set1 - set2` (差集)
子集检查set1.issubset(set2)set1 <= set2检查set1是否为set2的子集
python
# 集合创建示例
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)递归复制所有层级的对象,完全独立

示例:浅拷贝的“坑”

python
# 导入 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_copyoriginal 虽然是两个不同的列表对象,但它们内部的子列表 [1,2][3,4] 是同一个对象!所以改了浅拷贝里的子列表,原始列表自然也跟着变了。


示例:深拷贝的“解药”

python
# 导入 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] 也重新创建了一份新的列表对象。所以两者彻底独立,互不干扰。


注意事项

  1. 性能代价:深拷贝会递归遍历整个对象树,如果对象非常大或嵌套极深,性能开销会显著增加。不要滥用!
  2. 循环引用:如果对象内部存在循环引用(比如 A 包含 B,B 又包含 A),deepcopy 能正确处理,不会无限递归。
  3. 不可变对象无所谓:对于字符串、数字、元组(且内部元素也不可变)这类不可变对象,浅拷贝和深拷贝效果一样,因为根本没法改。
  4. 切片是浅拷贝new_list = old_list[:] 这种切片操作也是浅拷贝,同样有上述问题!

这节讲清楚了浅拷贝和深拷贝的本质区别,并通过实例展示了它们在嵌套容器中的不同行为,能帮你避开数据意外被修改的大坑。