1. 列表基础:Python程序员的第一把瑞士军刀
列表(List)是Python中最基础也最强大的数据结构之一。作为可变序列类型,它就像程序员的瑞士军刀,能容纳各种数据类型,随时增删改查。我刚开始学Python时,经常把列表和数组混为一谈,直到踩过几次坑才明白:Python列表远比传统数组灵活得多。
创建列表的几种常规方式:
python复制# 直接初始化
fruits = ['apple', 'banana', 'orange']
# 通过构造函数
numbers = list(range(1, 6))
# 列表推导式(后面会详解)
squares = [x**2 for x in numbers]
注意:列表可以包含不同类型的元素,比如
mixed = [1, 'a', True, 3.14]完全合法,但这通常不是好习惯,会导致后续处理麻烦。
列表的索引从0开始,支持负数索引(-1表示最后一个元素)。切片操作list[start:end:step]是必须掌握的技巧:
python复制nums = [0, 1, 2, 3, 4, 5, 6]
print(nums[1:5]) # [1, 2, 3, 4]
print(nums[::2]) # [0, 2, 4, 6]
print(nums[::-1]) # 反转列表
2. 列表操作:从基础到高阶的十八般武艺
2.1 增删改查基本功
添加元素有三种主要方式:
append(x):在末尾添加单个元素(时间复杂度O(1))extend(iterable):合并另一个可迭代对象(比+=更推荐)insert(i, x):在指定位置插入(慎用,时间复杂度O(n))
python复制colors = ['red', 'blue']
colors.append('green') # ['red', 'blue', 'green']
colors.extend(['yellow', 'purple']) # 合并新列表
colors.insert(1, 'orange') # 在索引1处插入
删除元素的常见方法:
remove(x):删除第一个匹配项pop([i]):删除并返回指定位置元素(默认最后一个)del语句:按索引或切片删除clear():清空列表
python复制colors.remove('blue') # 删除'blue'
last = colors.pop() # 删除并返回'purple'
del colors[1:3] # 切片删除
避坑指南:循环中修改列表是大忌!需要过滤元素时,应该创建新列表或倒序处理:
python复制# 错误示范 for item in lst: if condition(item): lst.remove(item) # 会导致跳过元素 # 正确做法 new_lst = [x for x in lst if not condition(x)]
2.2 排序与查找的艺术
列表排序有两种方式:
sort(key=None, reverse=False):原地排序sorted(iterable, key=None, reverse=False):返回新列表
python复制nums = [3, 1, 4, 2]
nums.sort() # 原地排序 [1, 2, 3, 4]
sorted_nums = sorted(nums, reverse=True) # 降序 [4, 3, 2, 1]
自定义排序用key参数:
python复制students = [('Alice', 88), ('Bob', 95), ('Charlie', 82)]
students.sort(key=lambda x: x[1]) # 按分数升序
查找元素:
index(x):返回第一个匹配项的索引count(x):统计出现次数in操作符:判断是否存在
python复制idx = nums.index(3) # 返回2
if 5 in nums: # 成员检查
print("存在")
3. 列表进阶:解锁Pythonic编程姿势
3.1 列表推导式:优雅的力量
列表推导式(List Comprehension)是Python最优雅的特性之一,能用一行代码完成循环和条件判断:
python复制# 生成1-10的平方
squares = [x**2 for x in range(1, 11)]
# 只保留偶数
evens = [x for x in range(20) if x % 2 == 0]
# 嵌套循环
matrix = [[1, 2], [3, 4]]
flatten = [num for row in matrix for num in row] # [1, 2, 3, 4]
性能提示:简单情况下推导式比普通循环快,但过于复杂的推导式会降低可读性,此时应该拆分成多行。
3.2 深拷贝与浅拷贝的陷阱
这是Python列表最易踩的坑之一:
python复制a = [1, 2, [3, 4]]
b = a.copy() # 浅拷贝
c = copy.deepcopy(a) # 深拷贝
a[2][0] = 999
print(b) # [1, 2, [999, 4]] 被影响
print(c) # [1, 2, [3, 4]] 不受影响
浅拷贝只复制最外层容器,深拷贝会递归复制所有嵌套对象。实际项目中,处理嵌套数据结构时务必明确需要哪种拷贝方式。
3.3 列表与迭代器
了解列表与迭代器的区别很重要:
- 列表:一次性加载所有元素到内存
- 迭代器:惰性计算,适合处理大数据
python复制# 列表(占用内存)
big_list = [x for x in range(1000000)]
# 生成器表达式(迭代器,节省内存)
big_iter = (x for x in range(1000000))
4. 性能优化:让列表操作快如闪电
4.1 时间复杂度实战分析
常见操作的时间复杂度:
- 索引/赋值:O(1)
- append/pop:O(1)
- insert/remove:O(n)
- 查找(x in list):O(n)
- 排序:O(n log n)
实际案例:合并多个列表
python复制# 低效做法(每次创建新列表)
result = []
for lst in list_of_lists:
result = result + lst # O(k^2)时间复杂度
# 高效做法
result = []
for lst in list_of_lists:
result.extend(lst) # O(n)时间复杂度
4.2 预分配空间技巧
当你知道最终列表大小时,预分配可以提升性能:
python复制# 普通append
result = []
for i in range(10000):
result.append(i) # 可能需要多次扩容
# 预分配
result = [None] * 10000
for i in range(10000):
result[i] = i
对于数值计算,考虑使用array模块或NumPy数组,它们比列表更节省内存且计算更快。
5. 实际应用案例:用列表解决现实问题
5.1 数据清洗实战
假设我们从CSV读取了包含空值和异常值的数据:
python复制raw_data = [23, 45, None, 72, 'N/A', 68, None, 90]
# 清洗步骤
clean_data = []
for x in raw_data:
if isinstance(x, int):
clean_data.append(x)
elif x is None:
clean_data.append(0)
# 忽略其他非数值类型
# 更Pythonic的写法
clean_data = [x if isinstance(x, int) else 0 for x in raw_data if x != 'N/A']
5.2 分组统计示例
对学生成绩进行分级统计:
python复制scores = [88, 92, 75, 60, 45, 96, 78, 82]
def grade(score):
if score >= 90: return 'A'
elif score >= 80: return 'B'
elif score >= 70: return 'C'
elif score >= 60: return 'D'
else: return 'F'
grade_counts = {}
for score in scores:
g = grade(score)
grade_counts[g] = grade_counts.get(g, 0) + 1
5.3 二维列表处理技巧
处理游戏棋盘或矩阵时:
python复制# 创建3x3棋盘
board = [[None]*3 for _ in range(3)] # 注意不能用[[None]*3]*3
# 转置矩阵
matrix = [[1, 2, 3], [4, 5, 6]]
transpose = [[row[i] for row in matrix] for i in range(3)]
6. 常见问题与解决方案
6.1 列表去重的N种方法
python复制original = [1, 2, 2, 3, 4, 4, 5]
# 方法1:利用集合(不保持顺序)
unique = list(set(original))
# 方法2:有序去重(Python 3.7+)
from collections import OrderedDict
unique = list(OrderedDict.fromkeys(original))
# 方法3:列表推导式(保持顺序)
seen = set()
unique = [x for x in original if not (x in seen or seen.add(x))]
6.2 多维列表初始化陷阱
python复制# 错误做法:所有行其实是同一个列表的引用
matrix = [[0]*3]*3
matrix[0][0] = 1 # 会修改所有行的第一个元素
# 正确做法
matrix = [[0 for _ in range(3)] for _ in range(3)]
6.3 列表与字符串转换
python复制# 字符串转列表
chars = list("hello") # ['h', 'e', 'l', 'l', 'o']
# 列表转字符串
words = ['Python', 'is', 'great']
sentence = ' '.join(words) # 'Python is great'
# 数字列表转字符串
nums = [1, 2, 3]
num_str = ''.join(map(str, nums)) # '123'
7. 性能对比:列表 vs 其他数据结构
7.1 列表与元组
- 列表:可变,适合需要修改的场景
- 元组:不可变,更安全且内存占用更小
python复制# 创建速度对比
%timeit [1, 2, 3] # 约100ns
%timeit (1, 2, 3) # 约15ns
# 内存占用对比
import sys
sys.getsizeof([1, 2, 3]) # 88 bytes (64位Python)
sys.getsizeof((1, 2, 3)) # 64 bytes
7.2 列表与集合
- 列表:保持顺序,允许重复
- 集合:自动去重,查找速度快(O(1))
python复制# 查找性能对比
lst = list(range(1000000))
s = set(lst)
%timeit 999999 in lst # 线性查找,约25ms
%timeit 999999 in s # 哈希查找,约50ns
7.3 何时选择其他数据结构
- 频繁查找:用集合或字典
- 先进先出:用
collections.deque - 数值计算:用
array.array或NumPy数组 - 不可变序列:用元组
8. 最佳实践与编程习惯
8.1 可读性优先
python复制# 不推荐
[x for x in lst if x%2==0 and x>10 and x<100]
# 推荐
def is_valid(x):
return x % 2 == 0 and 10 < x < 100
[x for x in lst if is_valid(x)]
8.2 防御性编程
python复制# 不安全的函数
def process_items(items):
for item in items:
print(item.upper())
# 更健壮的版本
def process_items(items):
if not isinstance(items, (list, tuple)):
raise TypeError("Expected list or tuple")
for item in items:
if hasattr(item, 'upper'):
print(item.upper())
else:
print(str(item))
8.3 文档与类型提示
python复制from typing import List, Union
def calculate_average(
numbers: List[Union[int, float]],
weights: List[float] = None
) -> float:
"""计算加权或简单平均数
Args:
numbers: 数值列表
weights: 可选权重列表,长度必须与numbers相同
Returns:
计算得到的平均值
"""
if weights is not None and len(weights) != len(numbers):
raise ValueError("Weights length must match numbers")
# ...实现代码...
9. 综合案例:用列表实现简单购物车
python复制class ShoppingCart:
def __init__(self):
self.items = []
self.prices = []
def add_item(self, name, price):
self.items.append(name)
self.prices.append(price)
def total(self):
return sum(self.prices)
def apply_discount(self, percent):
if not 0 <= percent <= 100:
raise ValueError("Discount must be between 0-100")
return self.total() * (100 - percent) / 100
def most_expensive(self):
if not self.prices:
return None
max_idx = self.prices.index(max(self.prices))
return self.items[max_idx]
# 使用示例
cart = ShoppingCart()
cart.add_item("Python书", 99)
cart.add_item("鼠标", 50)
print(f"总价: {cart.total()}") # 149
print(f"最贵商品: {cart.most_expensive()}") # Python书
10. 扩展思考:列表的底层实现
Python列表实际上是动态数组,当空间不足时会自动扩容。扩容策略是分配新内存(通常是当前大小的约1.125倍),然后复制元素。这就是为什么append操作平均时间复杂度是O(1)。
小技巧:查看列表的内存分配情况:
python复制import sys
lst = []
for i in range(20):
print(f"长度: {len(lst)}, 容量: {sys.getsizeof(lst)} bytes")
lst.append(i)
在实际项目中,如果处理超大数据(GB级别),应该考虑:
- 使用生成器代替列表
- 分块处理数据
- 使用专门的数据结构如
numpy.ndarray - 考虑使用数据库而不是内存列表