查看: 100|回复: 1

Python二维列表完全指南:创建、遍历、矩阵运算及深拷贝陷阱

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在Python开发中,一维列表能解决的问题有限,而二维列表(嵌套列表)才是处理表格、棋盘、图像等结构化数据的核心工具。本文将从创建、访问、修改、矩阵运算到深拷贝陷阱,系统梳理二维列表的实战技巧,并提供可直接运行的代码示例。

一、创建二维列表的正确姿势

1. 字面量创建(适用于小型固定数据)
  1. matrix = [
  2.     [1, 2, 3],
  3.     [4, 5, 6],
  4.     [7, 8, 9]
  5. ]
  6. print(matrix[0])      # [1, 2, 3]
  7. print(matrix[1][2])   # 6
  8. print(len(matrix))    # 3
  9. print(len(matrix[0])) # 3
复制代码

2. 推导式创建(推荐)
推导式能生成规则矩阵,且每个内层列表都是独立对象。
  1. # 3行4列全零矩阵
  2. zeros = [[0 for _ in range(4)] for _ in range(3)]
  3. print(zeros)
  4. # 乘法表
  5. table = [[i * j for j in range(1, 6)] for i in range(1, 6)]
  6. for row in table:
  7.     print(row)
复制代码

3. 经典陷阱:乘法创建
  1. wrong = [[0] * 4] * 3
  2. wrong[0][0] = 999
  3. print(wrong)  # [[999, 0, 0, 0], [999, 0, 0, 0], [999, 0, 0, 0]]
  4. # 三行都变了!因为 *3 复制的是同一内层列表的引用
  5. print(wrong[0] is wrong[1])  # True
  6. # 正确做法
  7. correct = [[0] * 4 for _ in range(3)]
  8. correct[0][0] = 999
  9. print(correct)  # [[999, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
  10. # 只有第一行变化
复制代码
乘法创建造成的“引用共享”是Python新手最易踩的坑,务必备注使用推导式。

4. 不规则(锯齿)二维列表
  1. jagged = [
  2.     [1, 2, 3],
  3.     [4, 5],
  4.     [6, 7, 8, 9]
  5. ]
  6. for i, row in enumerate(jagged):
  7.     print(f'第{i}行有{len(row)}列')
复制代码

二、访问与遍历

1. 索引操作
  1. matrix = [
  2.     [1, 2, 3, 4],
  3.     [5, 6, 7, 8],
  4.     [9, 10, 11, 12]
  5. ]
  6. print(matrix[0][0])   # 1
  7. print(matrix[1][2])   # 7
  8. print(matrix[-1][-1]) # 12
  9. print(matrix[0])      # 整行
  10. col_0 = [row[0] for row in matrix]  # 整列
  11. print(col_0)          # [1, 5, 9]
复制代码

2. 遍历推荐(enumerate双重循环)
  1. for i, row in enumerate(matrix):
  2.     for j, value in enumerate(row):
  3.         print(f'matrix[{i}][{j}] = {value}')
复制代码

3. 行列切片与子矩阵
  1. print(matrix[1][:2])  # [5, 6]
  2. def submatrix(mat, rs, re, cs, ce):
  3.     return [row[cs:ce] for row in mat[rs:re]]
复制代码

三、修改二维列表

1. 修改单个元素或整行
  1. matrix[1][2] = 99
  2. matrix[0] = [10, 20, 30]
  3. # 修改整列需循环
  4. for row in matrix:
  5.     row[1] = 0
复制代码

2. 增删行列
  1. matrix.append([7, 8, 9])          # 加一行
  2. for i, row in enumerate(matrix):  # 加一列
  3.     row.append((i + 1) * 10)
  4. del matrix[1]                     # 删一行
  5. for row in matrix:                # 删一列
  6.     del row[-1]
  7. matrix.insert(1, [100, 200, 300]) # 插入一行
复制代码

四、矩阵运算实战

1. 转置
  1. def transpose(mat):
  2.     if not mat:
  3.         return []
  4.     rows, cols = len(mat), len(mat[0])
  5.     return [[mat[i][j] for i in range(rows)] for j in range(cols)]
  6. # 或用 zip 简洁实现
  7. transposed = [list(col) for col in zip(*matrix)]
复制代码

2. 加法与数乘
  1. def matrix_add(a, b):
  2.     rows, cols = len(a), len(a[0])
  3.     return [[a[i][j] + b[i][j] for j in range(cols)] for i in range(rows)]
  4. def matrix_scalar_mul(mat, scalar):
  5.     return [[cell * scalar for cell in row] for row in mat]
复制代码

3. 矩阵乘法
  1. def matrix_multiply(a, b):
  2.     if not a or not b:
  3.         return []
  4.     if len(a[0]) != len(b):
  5.         raise ValueError('矩阵尺寸不匹配')
  6.     rows_a, cols_a = len(a), len(a[0])
  7.     cols_b = len(b[0])
  8.     result = [[0] * cols_b for _ in range(rows_a)]
  9.     for i in range(rows_a):
  10.         for j in range(cols_b):
  11.             result[i][j] = sum(a[i][k] * b[k][j] for k in range(cols_a))
  12.     return result
  13. # 验证:2×3 乘 3×2 得 2×2
  14. a = [[1, 2, 3], [4, 5, 6]]
  15. b = [[7, 8], [9, 10], [11, 12]]
  16. c = matrix_multiply(a, b)
  17. print(c)  # [[58, 64], [139, 154]]
复制代码

五、棋盘游戏经典应用

1. 井字棋(Tic-Tac-Toe)
二维列表天然适合表示3×3棋盘,以下简化版状态机可实现胜负判断与落子。
  1. class TicTacToe:
  2.     def __init__(self):
  3.         self.board = [[' ' for _ in range(3)] for _ in range(3)]
  4.         self.current_player = 'X'
  5.     def display(self):
  6.         print('  0 1 2')
  7.         for i, row in enumerate(self.board):
  8.             print(f'{i} ' + ' | '.join(row))
  9.             if i < 2:
  10.                 print(' ---+---+---')
  11.     def make_move(self, row, col):
  12.         if self.board[row][col] != ' ':
  13.             return False
  14.         self.board[row][col] = self.current_player
  15.         self.current_player = 'O' if self.current_player == 'X' else 'X'
  16.         return True
  17.     def check_winner(self):
  18.         b = self.board
  19.         # 检查行、列、对角线
  20.         for row in b:
  21.             if row[0] == row[1] == row[2] != ' ':
  22.                 return row[0]
  23.         for col in range(3):
  24.             if b[0][col] == b[1][col] == b[2][col] != ' ':
  25.                 return b[0][col]
  26.         if b[0][0] == b[1][1] == b[2][2] != ' ':
  27.             return b[0][0]
  28.         if b[0][2] == b[1][1] == b[2][0] != ' ':
  29.             return b[0][2]
  30.         if all(cell != ' ' for row in b for cell in row):
  31.             return '平局'
  32.         return None
复制代码

2. 扫雷棋盘生成
随机布雷后,用方向数组计算周围雷数。
  1. import random
  2. def create_minesweeper(rows, cols, mines):
  3.     board = [[0 for _ in range(cols)] for _ in range(rows)]
  4.     mine_positions = set()
  5.     while len(mine_positions) < mines:
  6.         r = random.randint(0, rows - 1)
  7.         c = random.randint(0, cols - 1)
  8.         mine_positions.add((r, c))
  9.         board[r][c] = -1
  10.     directions = [(-1,-1), (-1,0), (-1,1), (0,-1),
  11.                   (0,1), (1,-1), (1,0), (1,1)]
  12.     for r, c in mine_positions:
  13.         for dr, dc in directions:
  14.             nr, nc = r + dr, c + dc
  15.             if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] != -1:
  16.                 board[nr][nc] += 1
  17.     return board
  18. board = create_minesweeper(8, 8, 10)
  19. for row in board:
  20.     print(' '.join(f'{cell:2d}' for cell in row))
复制代码

六、数据表格处理

1. CSV解析与排序
  1. def parse_csv(text, delimiter=','):
  2.     lines = text.strip().split('\n')
  3.     return [line.split(delimiter) for line in lines if line.strip()]
  4. csv_text = '''姓名,年龄,城市,职业
  5. 小明,25,北京,工程师
  6. 小红,23,上海,设计师
  7. 小刚,26,广州,分析师'''
  8. data = parse_csv(csv_text)
  9. def sort_by_column(data, col_index, numeric=False):
  10.     headers = data[0]
  11.     body = data[1:]
  12.     key_fn = lambda row: float(row[col_index]) if numeric else row[col_index]
  13.     body.sort(key=key_fn)
  14.     return [headers] + body
  15. sorted_data = sort_by_column(data, 1, numeric=True)
  16. for row in sorted_data:
  17.     print(row)
复制代码

2. 数据透视表模拟
  1. def pivot_table(data, row_field, col_field, value_field, agg_func=sum):
  2.     row_values = sorted(set(d[row_field] for d in data))
  3.     col_values = sorted(set(d[col_field] for d in data))
  4.     pivot = [[0 for _ in range(len(col_values))] for _ in range(len(row_values))]
  5.     row_idx = {v: i for i, v in enumerate(row_values)}
  6.     col_idx = {v: i for i, v in enumerate(col_values)}
  7.     groups = {}
  8.     for record in data:
  9.         key = (record[row_field], record[col_field])
  10.         groups.setdefault(key, []).append(record[value_field])
  11.     for (r, c), values in groups.items():
  12.         pivot[row_idx[r]][col_idx[c]] = agg_func(values)
  13.     return pivot, row_values, col_values
复制代码

七、三维列表与更深的嵌套

三维列表由多个二维平面堆叠而成,创建时仍需防范引用共享。
  1. depth, rows, cols = 2, 3, 4
  2. cube = [[[0 for _ in range(cols)] for _ in range(rows)] for _ in range(depth)]
  3. cube[0][1][2] = 99
  4. print(cube[0][1][2])  # 99
  5. for p, plane in enumerate(cube):
  6.     print(f'平面 {p}:')
  7.     for row in plane:
  8.         print(row)
复制代码

八、深拷贝与浅拷贝陷阱

嵌套列表的复制必须注意:浅拷贝只复制外层,内层列表共享引用。
  1. import copy
  2. original = [[1, 2], [3, 4]]
  3. shallow = original[:]       # 浅拷贝
  4. shallow[0][0] = 999
  5. print(original)  # [[999, 2], [3, 4]] 已被修改
  6. deep = copy.deepcopy(original)
  7. deep[0][0] = 888
  8. print(original)  # [[999, 2], [3, 4]] 不受影响
复制代码
如果不希望引入copy模块,可手动深拷贝二维列表:
  1. [row[:] for row in original]
复制代码


总结:创建二维列表永远使用推导式;遍历推荐enumerate双重循环;矩阵运算掌握转置、加法、乘法;数据表格可用列表模拟CSV和透视;深拷贝是避免意外修改的关键。掌握这些技巧,二维列表将成为你处理结构化数据的得力工具。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python二维列表完全指南:创建、遍历、矩阵运算及深拷贝陷阱

很棒的文章!从创建到矩阵运算都讲得很清晰,特别是那个乘法创建陷阱的示例,直接对比 `wrong` 和 `correct` 的效果,新手一看就能明白为什么不能用 `[[0]*4]*3`。 不过标题里提到了“深拷贝陷阱”,正文中主要讲了乘法造成的引用共享,如果能把 `copy.deepcopy` 和 `copy.copy` 对二维列表的影响也加进来,比如直接用 `.copy()` 复制外层列表后内层还是同一引用,那就更完整了。当然,现在的讲解已经很实用了,特别是 `submatrix` 函数和用 `zip` 实现转置的技巧,直接拿来就能用。感谢分享!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-15 13:07 , Processed in 0.027435 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部