数据结构高级操作|第三部分:流程控制与数据结构操作 (Control Flow & Data Structures Manipulation)
欢迎来到你的“兵器”精通课程!在之前的旅程中,我们认识了字符串、列表、字典等基本“兵器”。现在,你已经学会了 if
和 for
这些内功心法,是时候将它们与兵器结合,施展出令人眼花缭乱的“高级招式”了。
本章将带你深入每一种数据结构的内部,探索那些能让你代码效率倍增、可读性飙升的技巧。你将学会如何像切蛋糕一样精准地切割数据(切片),如何用一行代码创造一个列表(推导式),以及如何复制数据而不引发“连锁反应”(深浅拷贝)。这些技巧,是区分Python新手和高手的真正分水岭。准备好,让你的数据处理能力发生质的飞跃吧!
7.1 字符串: 不只是文本,更是艺术
字符串是不可变的,但这并不妨碍我们对它进行各种花哨的操作,每次操作都会产生一个新的、为你量身定制的字符串。
-
索引与切片 (Slicing): 这是Python最优雅的特性之一。你可以像取书架上的书一样,精确地取出字符串的任何部分。
- 索引:
string[index]
,从0开始。负数索引表示从后往前数,-1
是最后一个字符。 - 切片:
string[start:stop:step]
,一个“顾头不顾尾”的操作,包含start
索引,但不包含stop
索引。
s = "Hello, Python!" # 索引 print(s[0]) # 输出: H print(s[-1]) # 输出: ! # 切片 print(s[7:13]) # 输出: Python (从索引7到12) print(s[:5]) # 输出: Hello (从开头到索引4) print(s[7:]) # 输出: Python! (从索引7到结尾) print(s[::-1]) # 终极技巧:步长为-1,轻松反转字符串! -> !nohtyP ,olleH
- 索引:
-
格式化 (f-string): 这是现代Python(3.6+)中最推荐的字符串格式化方式。它就像一个“填空题”模板,直观、高效、强大。
name = "Alice" age = 30 # 使用 f-string message = f"大家好,我叫 {name},我今年 {age} 岁了。" print(message) # 输出: 大家好,我叫 Alice,我今年 30 岁了。 # 甚至可以在 {} 中执行简单的表达式 print(f"{name} 明年就 {age + 1} 岁了。") # 输出: Alice 明年就 31 岁了。
-
常用方法: 字符串自带了大量超级有用的“工具函数”。
.strip()
: 去除首尾的空白字符。.upper()
/.lower()
: 转换为大写/小写。.replace(old, new)
: 替换子字符串。.split(separator)
: 按分隔符分割字符串,返回一个列表。separator.join(iterable)
: 用指定分隔符连接一个序列中的所有元素。
text = " python is awesome " print(text.strip()) # 输出: "python is awesome" print(text.upper()) # 输出: " PYTHON IS AWESOME " print(text.replace("awesome", "fun")) # 输出: " python is fun " csv_data = "apple,banana,cherry" fruits_list = csv_data.split(',') print(fruits_list) # 输出: ['apple', 'banana', 'cherry'] print(" | ".join(fruits_list)) # 输出: "apple | banana | cherry"
7.2 列表: 你的动态数据瑞士军刀
列表是可变的,这意味着我们可以随心所欲地对它进行增、删、改、查。
-
增删改查 (CRUD)
- 增:
.append(item)
(在末尾添加),.insert(index, item)
(在指定位置插入) - 删:
.pop(index)
(删除并返回指定位置元素,默认为最后一个),.remove(value)
(删除第一个匹配的元素),del my_list[index]
(删除指定位置元素) - 改:
my_list[index] = new_value
- 查:
my_list[index]
nums = [1, 2, 3] nums.append(4) # [1, 2, 3, 4] nums.insert(1, 99) # [1, 99, 2, 3, 4] removed_item = nums.pop() # removed_item is 4, nums is [1, 99, 2, 3] nums.remove(99) # [1, 2, 3] nums[1] = 200 # [1, 200, 3] print(nums)
- 增:
-
排序:
my_list.sort()
: 原地排序,会直接修改原始列表。sorted(my_list)
: 返回一个新的排好序的列表,不改变原始列表。
scores = [88, 56, 97, 73] # 使用 sorted() sorted_scores = sorted(scores) print(f"原始列表: {scores}") # 输出: [88, 56, 97, 73] print(f"排序后的新列表: {sorted_scores}") # 输出: [56, 73, 88, 97] # 使用 .sort() scores.sort(reverse=True) # reverse=True 实现降序排序 print(f"原地排序后的列表: {scores}") # 输出: [97, 88, 73, 56]
-
复制(浅拷贝与深拷贝): 这是一个非常重要的概念!
- 直接赋值:
b = a
,这只是给同一个列表起了个别名,修改b
会影响a
。 - 浅拷贝 (Shallow Copy):
b = a.copy()
或b = a[:]
。会创建一个新列表,但如果列表内包含其他可变对象(如子列表),则只复制这些对象的引用。 - 深拷贝 (Deep Copy): 需要
import copy; b = copy.deepcopy(a)
。会递归地复制所有内容,创建一个完全独立的新对象。
# 浅拷贝的“陷阱” import copy list_a = [1, 2, [10, 20]] list_b = list_a.copy() # 浅拷贝 list_b.append(3) # 修改 list_b 的外层,不影响 list_a list_b[2].append(30) # 修改 list_b 中嵌套列表的内容 print(f"List A: {list_a}") # 输出: List A: [1, 2, [10, 20, 30]] <-- 被影响了! print(f"List B: {list_b}") # 输出: List B: [1, 2, [10, 20, 30], 3] # 深拷贝解决问题 list_c = copy.deepcopy(list_a) list_c[2].append(40) print(f"List A (after deepcopy change): {list_a}") # 输出: List A (after deepcopy change): [1, 2, [10, 20, 30]] <-- 未受影响! print(f"List C: {list_c}") # 输出: List C: [1, 2, [10, 20, 30, 40]]
- 直接赋值:
-
列表推导式 (List Comprehension): Python的“语法糖”之王!它能让你用一行代码,基于现有列表创建一个新列表,优雅且高效。
基本语法:[expression for item in iterable if condition]
# 传统方法:创建一个0-9的平方数列表 squares = [] for i in range(10): squares.append(i * i) print(squares) # 列表推导式:一行搞定! squares_comp = [i * i for i in range(10)] print(squares_comp) # 加上条件:只取偶数的平方 even_squares = [i * i for i in range(10) if i % 2 == 0] print(even_squares) # 输出: [0, 4, 16, 36, 64]
7.3 元组: 不可变性带来的妙用
虽然元组不能修改,但它在“解构”数据方面有奇效。
-
打包与解包 (Packing & Unpacking):
- 打包: 将多个值“打包”成一个元组。
my_tuple = 1, "a", True
- 解包: 将元组中的元素一次性赋给多个变量。
# 打包 point = (100, 200) # 解包 x, y = point print(f"x坐标: {x}, y坐标: {y}") # 输出: x坐标: 100, y坐标: 200 # 这个技巧常用于函数返回多个值 def get_user_info(): return "Bob", 35, "USA" name, age, country = get_user_info()
- 打包: 将多个值“打包”成一个元组。
-
命名元组 (
namedtuple
): 当你觉得用point[0]
point[1]
这样的索引来访问元组元素不够直观时,namedtuple
就是你的救星。它能给元组的每个位置起个名字。from collections import namedtuple # 创建一个名为 "Point" 的命名元组模板 Point = namedtuple("Point", ["x", "y"]) # 使用模板创建实例 p1 = Point(10, 20) print(p1.x) # 输出: 10 (比 p1[0] 可读性强多了!) print(p1.y) # 输出: 20
7.4 字典: 键值对的终极玩法
-
增删改查:
- 查:
my_dict.get(key, default_value)
是比my_dict[key]
更安全的方式,当键不存在时,它不会报错,而是返回你指定的默认值(默认为None
)。 - 删:
del my_dict[key]
或my_dict.pop(key)
。
person = {"name": "Charlie"} print(person.get("age", 18)) # 输出: 18 (age不存在,返回默认值18) # print(person["age"]) # 这行会直接报错 KeyError
- 查:
-
遍历:
for
循环可以优雅地遍历字典。.keys()
: 遍历所有的键。.values()
: 遍历所有的值。.items()
: 最常用,同时遍历键和值。
user = {"name": "David", "id": 101, "role": "admin"} # 遍历键值对 for key, value in user.items(): print(f"{key}: {value}")
-
视图对象 (
keys()
,values()
,items()
): 这些方法返回的不是列表,而是一个“视图”。视图是动态的,如果原始字典发生变化,视图会立即反映这些变化。 -
嵌套字典: 字典可以包含其他字典,形成复杂的数据结构,这在处理JSON等数据时非常常见。
users = { "user1": {"name": "Eve", "age": 22}, "user2": {"name": "Frank", "age": 29} } print(users["user2"]["name"]) # 输出: Frank
7.5 集合: 数学运算的利器
集合的核心在于它的数学特性:并集、交集、差集等。
-
增删改查:
.add(item)
,.remove(item)
(若元素不存在会报错),.discard(item)
(若元素不存在不会报错)。 -
数学运算:
- 并集:
set_a | set_b
或set_a.union(set_b)
- 交集:
set_a & set_b
或set_a.intersection(set_b)
- 差集:
set_a - set_b
或set_a.difference(set_b)
- 对称差集:
set_a ^ set_b
或set_a.symmetric_difference(set_b)
(两个集合中不重复的元素)
devs = {"Alice", "Bob", "Charlie"} ops = {"Charlie", "David", "Eve"} print(f"所有员工: {devs | ops}") print(f"既是开发又是运维: {devs & ops}") print(f"只是开发人员: {devs - ops}") print(f"只属于一个部门的员工: {devs ^ ops}")
- 并集:
7.6 数组: array
模块的使用
Python的列表可以存放不同类型的元素,非常灵活,但这也意味着额外的内存开销。如果你需要存储大量的同类型数值数据(如整数或浮点数),使用 array
模块会更节省内存。
注意: 在数据科学领域,NumPy
库的 array
是绝对的主流和首选。这里的 array
模块是Python内置的一个轻量级替代品。
import array
# 'i' 是一个类型码,表示有符号整数
int_array = array.array('i', [1, 2, 3, 4, 5])
print(int_array)
print(int_array[2]) # 输出: 3
# 尝试添加一个非整数类型
# int_array.append(3.14) # 这会引发 TypeError
难以置信!你已经从一个数据结构的“使用者”升级为了“驾驭者”。你现在拥有的这些高级技巧——切片、推导式、深拷贝、解包等等——将使你的代码更加简洁、高效和专业。你已经具备了处理复杂数据的坚实基础。
但是,当我们写的代码越来越多,逻辑越来越复杂时,如何将这些强大的操作组织起来,让它们可以被复用,让整个项目保持清晰和整洁呢?答案就是函数和模块。在下一章,我们将学习如何将代码打包成可重用的“积木”,开启构建大型程序的宏伟篇章!准备好,成为一名真正的“代码建筑师”吧!