在Python开发中,一维列表能解决的问题有限,而二维列表(嵌套列表)才是处理表格、棋盘、图像等结构化数据的核心工具。本文将从创建、访问、修改、矩阵运算到深拷贝陷阱,系统梳理二维列表的实战技巧,并提供可直接运行的代码示例。
一、创建二维列表的正确姿势
1. 字面量创建(适用于小型固定数据)- matrix = [
- [1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]
- ]
- print(matrix[0]) # [1, 2, 3]
- print(matrix[1][2]) # 6
- print(len(matrix)) # 3
- print(len(matrix[0])) # 3
复制代码
2. 推导式创建(推荐)
推导式能生成规则矩阵,且每个内层列表都是独立对象。- # 3行4列全零矩阵
- zeros = [[0 for _ in range(4)] for _ in range(3)]
- print(zeros)
- # 乘法表
- table = [[i * j for j in range(1, 6)] for i in range(1, 6)]
- for row in table:
- print(row)
复制代码
3. 经典陷阱:乘法创建- wrong = [[0] * 4] * 3
- wrong[0][0] = 999
- print(wrong) # [[999, 0, 0, 0], [999, 0, 0, 0], [999, 0, 0, 0]]
- # 三行都变了!因为 *3 复制的是同一内层列表的引用
- print(wrong[0] is wrong[1]) # True
- # 正确做法
- correct = [[0] * 4 for _ in range(3)]
- correct[0][0] = 999
- print(correct) # [[999, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
- # 只有第一行变化
复制代码 乘法创建造成的“引用共享”是Python新手最易踩的坑,务必备注使用推导式。
4. 不规则(锯齿)二维列表- jagged = [
- [1, 2, 3],
- [4, 5],
- [6, 7, 8, 9]
- ]
- for i, row in enumerate(jagged):
- print(f'第{i}行有{len(row)}列')
复制代码
二、访问与遍历
1. 索引操作- matrix = [
- [1, 2, 3, 4],
- [5, 6, 7, 8],
- [9, 10, 11, 12]
- ]
- print(matrix[0][0]) # 1
- print(matrix[1][2]) # 7
- print(matrix[-1][-1]) # 12
- print(matrix[0]) # 整行
- col_0 = [row[0] for row in matrix] # 整列
- print(col_0) # [1, 5, 9]
复制代码
2. 遍历推荐(enumerate双重循环)- for i, row in enumerate(matrix):
- for j, value in enumerate(row):
- print(f'matrix[{i}][{j}] = {value}')
复制代码
3. 行列切片与子矩阵- print(matrix[1][:2]) # [5, 6]
- def submatrix(mat, rs, re, cs, ce):
- return [row[cs:ce] for row in mat[rs:re]]
复制代码
三、修改二维列表
1. 修改单个元素或整行- matrix[1][2] = 99
- matrix[0] = [10, 20, 30]
- # 修改整列需循环
- for row in matrix:
- row[1] = 0
复制代码
2. 增删行列- matrix.append([7, 8, 9]) # 加一行
- for i, row in enumerate(matrix): # 加一列
- row.append((i + 1) * 10)
- del matrix[1] # 删一行
- for row in matrix: # 删一列
- del row[-1]
- matrix.insert(1, [100, 200, 300]) # 插入一行
复制代码
四、矩阵运算实战
1. 转置- def transpose(mat):
- if not mat:
- return []
- rows, cols = len(mat), len(mat[0])
- return [[mat[i][j] for i in range(rows)] for j in range(cols)]
- # 或用 zip 简洁实现
- transposed = [list(col) for col in zip(*matrix)]
复制代码
2. 加法与数乘- def matrix_add(a, b):
- rows, cols = len(a), len(a[0])
- return [[a[i][j] + b[i][j] for j in range(cols)] for i in range(rows)]
- def matrix_scalar_mul(mat, scalar):
- return [[cell * scalar for cell in row] for row in mat]
复制代码
3. 矩阵乘法- def matrix_multiply(a, b):
- if not a or not b:
- return []
- if len(a[0]) != len(b):
- raise ValueError('矩阵尺寸不匹配')
- rows_a, cols_a = len(a), len(a[0])
- cols_b = len(b[0])
- result = [[0] * cols_b for _ in range(rows_a)]
- for i in range(rows_a):
- for j in range(cols_b):
- result[i][j] = sum(a[i][k] * b[k][j] for k in range(cols_a))
- return result
- # 验证:2×3 乘 3×2 得 2×2
- a = [[1, 2, 3], [4, 5, 6]]
- b = [[7, 8], [9, 10], [11, 12]]
- c = matrix_multiply(a, b)
- print(c) # [[58, 64], [139, 154]]
复制代码
五、棋盘游戏经典应用
1. 井字棋(Tic-Tac-Toe)
二维列表天然适合表示3×3棋盘,以下简化版状态机可实现胜负判断与落子。- class TicTacToe:
- def __init__(self):
- self.board = [[' ' for _ in range(3)] for _ in range(3)]
- self.current_player = 'X'
- def display(self):
- print(' 0 1 2')
- for i, row in enumerate(self.board):
- print(f'{i} ' + ' | '.join(row))
- if i < 2:
- print(' ---+---+---')
- def make_move(self, row, col):
- if self.board[row][col] != ' ':
- return False
- self.board[row][col] = self.current_player
- self.current_player = 'O' if self.current_player == 'X' else 'X'
- return True
- def check_winner(self):
- b = self.board
- # 检查行、列、对角线
- for row in b:
- if row[0] == row[1] == row[2] != ' ':
- return row[0]
- for col in range(3):
- if b[0][col] == b[1][col] == b[2][col] != ' ':
- return b[0][col]
- if b[0][0] == b[1][1] == b[2][2] != ' ':
- return b[0][0]
- if b[0][2] == b[1][1] == b[2][0] != ' ':
- return b[0][2]
- if all(cell != ' ' for row in b for cell in row):
- return '平局'
- return None
复制代码
2. 扫雷棋盘生成
随机布雷后,用方向数组计算周围雷数。- import random
- def create_minesweeper(rows, cols, mines):
- board = [[0 for _ in range(cols)] for _ in range(rows)]
- mine_positions = set()
- while len(mine_positions) < mines:
- r = random.randint(0, rows - 1)
- c = random.randint(0, cols - 1)
- mine_positions.add((r, c))
- board[r][c] = -1
- directions = [(-1,-1), (-1,0), (-1,1), (0,-1),
- (0,1), (1,-1), (1,0), (1,1)]
- for r, c in mine_positions:
- for dr, dc in directions:
- nr, nc = r + dr, c + dc
- if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] != -1:
- board[nr][nc] += 1
- return board
- board = create_minesweeper(8, 8, 10)
- for row in board:
- print(' '.join(f'{cell:2d}' for cell in row))
复制代码
六、数据表格处理
1. CSV解析与排序- def parse_csv(text, delimiter=','):
- lines = text.strip().split('\n')
- return [line.split(delimiter) for line in lines if line.strip()]
- csv_text = '''姓名,年龄,城市,职业
- 小明,25,北京,工程师
- 小红,23,上海,设计师
- 小刚,26,广州,分析师'''
- data = parse_csv(csv_text)
- def sort_by_column(data, col_index, numeric=False):
- headers = data[0]
- body = data[1:]
- key_fn = lambda row: float(row[col_index]) if numeric else row[col_index]
- body.sort(key=key_fn)
- return [headers] + body
- sorted_data = sort_by_column(data, 1, numeric=True)
- for row in sorted_data:
- print(row)
复制代码
2. 数据透视表模拟- def pivot_table(data, row_field, col_field, value_field, agg_func=sum):
- row_values = sorted(set(d[row_field] for d in data))
- col_values = sorted(set(d[col_field] for d in data))
- pivot = [[0 for _ in range(len(col_values))] for _ in range(len(row_values))]
- row_idx = {v: i for i, v in enumerate(row_values)}
- col_idx = {v: i for i, v in enumerate(col_values)}
- groups = {}
- for record in data:
- key = (record[row_field], record[col_field])
- groups.setdefault(key, []).append(record[value_field])
- for (r, c), values in groups.items():
- pivot[row_idx[r]][col_idx[c]] = agg_func(values)
- return pivot, row_values, col_values
复制代码
七、三维列表与更深的嵌套
三维列表由多个二维平面堆叠而成,创建时仍需防范引用共享。- depth, rows, cols = 2, 3, 4
- cube = [[[0 for _ in range(cols)] for _ in range(rows)] for _ in range(depth)]
- cube[0][1][2] = 99
- print(cube[0][1][2]) # 99
- for p, plane in enumerate(cube):
- print(f'平面 {p}:')
- for row in plane:
- print(row)
复制代码
八、深拷贝与浅拷贝陷阱
嵌套列表的复制必须注意:浅拷贝只复制外层,内层列表共享引用。- import copy
- original = [[1, 2], [3, 4]]
- shallow = original[:] # 浅拷贝
- shallow[0][0] = 999
- print(original) # [[999, 2], [3, 4]] 已被修改
- deep = copy.deepcopy(original)
- deep[0][0] = 888
- print(original) # [[999, 2], [3, 4]] 不受影响
复制代码 如果不希望引入copy模块,可手动深拷贝二维列表:- [row[:] for row in original]
复制代码 。
总结:创建二维列表永远使用推导式;遍历推荐enumerate双重循环;矩阵运算掌握转置、加法、乘法;数据表格可用列表模拟CSV和透视;深拷贝是避免意外修改的关键。掌握这些技巧,二维列表将成为你处理结构化数据的得力工具。 |