python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python二维列表操作

Python基础指南之二维列表(列表嵌套)的操作技巧

作者:星河耀银海

嵌套列表(Nested List)就是列表里面套列表,最常见的形态就是二维列表,本文会详细介绍Python中二维列表(嵌套列表)的创建、访问和操作方法,感兴趣的小伙伴可以了解下

一、开篇:一层列表不够用的时候

前面几篇文章我们把一维列表(简单列表)的各种操作学了个遍。但在实际开发中,你遇到的数据结构往往不止一层。电子表格是二维的(行×列),游戏棋盘是二维的,图片像素也是二维的——这些场景都需要嵌套列表

嵌套列表(Nested List)就是"列表里面套列表"。最常见的形态是二维列表——一个列表,其中每个元素又是一个列表,形成行和列的结构。但嵌套可以更深:三维列表用于体素游戏、四维列表用于科学计算……理解嵌套列表的创建、访问、修改和遍历,是处理结构化数据的基本功。

这篇文章我们会从二维列表的创建开始,讲到矩阵运算、棋盘游戏、数据透 视表等实战应用,还会特别强调嵌套列表中那些让人掉坑的"引用共享"问题。

二、二维列表的创建方式

2.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(3行)
print(len(matrix[0]))  # 3(第一行有3列)

2.2 用推导式创建(推荐方式)

# 方式一:推导式——创建规则矩阵
# 3行4列的全零矩阵
zeros = [[0 for _ in range(4)] for _ in range(3)]
print(zeros)  # [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

# 生成乘法表
table = [[i * j for j in range(1, 6)] for i in range(1, 6)]
for row in table:
    print(row)
# [1, 2, 3, 4, 5]
# [2, 4, 6, 8, 10]
# [3, 6, 9, 12, 15]
# [4, 8, 12, 16, 20]
# [5, 10, 15, 20, 25]

# 生成单位矩阵
n = 4
identity = [[1 if i == j else 0 for j in range(n)] for i in range(n)]
for row in identity:
    print(row)
# [1, 0, 0, 0]
# [0, 1, 0, 0]
# [0, 0, 1, 0]
# [0, 0, 0, 1]

2.3 经典陷阱:乘法创建二维列表

# 这是Python中最臭名昭著的陷阱之一!

# 错误写法——三行都指向同一个内部列表!
wrong = [[0] * 4] * 3
print(wrong)  # [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
wrong[0][0] = 999
print(wrong)  # [[999, 0, 0, 0], [999, 0, 0, 0], [999, 0, 0, 0]]
# 三行全部变了!因为三行是同一个列表的引用

# 验证三个"行"是否是同一个对象
print(wrong[0] is wrong[1])  # True
print(wrong[1] is wrong[2])  # True
print(id(wrong[0]), id(wrong[1]), id(wrong[2]))  # 三个id相同!

# 正确写法——每一行都是独立创建的新列表
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]]
# 只有第一行变了!

print(correct[0] is correct[1])  # False(不同的对象)

为什么 [[0]*4]*3 不行?因为 [[0]*4] 创建了一个内部列表 [0,0,0,0],然后 *3 把这个列表的引用复制了三份。三行指向内存中的同一个列表。永远用推导式创建嵌套列表,不要用乘法

2.4 创建不规则二维列表

# 二维列表不一定是矩形的(锯齿状/不规则)
jagged = [
    [1, 2, 3],
    [4, 5],
    [6, 7, 8, 9],
]

# 访问不同行的列数
for i, row in enumerate(jagged):
    print(f'第{i}行有{len(row)}列: {row}')

# 锯齿列表在表示层次结构时很有用
# 例如:部门→员工
departments = [
    ['张总'],
    ['李经理', '王经理'],
    ['小赵', '小钱', '小孙', '小李'],
]

三、二维列表的访问与遍历

3.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[2][3])   # 12(第三行第四列)
print(matrix[-1][-1]) # 12(最后一行最后一列)

# 访问整行
print(matrix[0])      # [1, 2, 3, 4]

# 访问整列(用推导式)
col_0 = [row[0] for row in matrix]
print(col_0)  # [1, 5, 9]

col_2 = [row[2] for row in matrix]
print(col_2)  # [3, 7, 11]

3.2 遍历方式

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

# 方式一:逐行遍历(最简单)
for row in matrix:
    print(row)

# 方式二:行列双重循环
for i in range(len(matrix)):          # 遍历行
    for j in range(len(matrix[i])):   # 遍历列
        print(f'matrix[{i}][{j}] = {matrix[i][j]}')

# 方式三:enumerate双重循环(推荐,最Pythonic)
for i, row in enumerate(matrix):
    for j, value in enumerate(row):
        print(f'matrix[{i}][{j}] = {value}')

# 方式四:逐个元素展开遍历
for value in [cell for row in matrix for cell in row]:
    print(value, end=' ')  # 1 2 3 4 5 6 7 8 9

3.3 行列操作

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

# 获取对角线元素
main_diag = [matrix[i][i] for i in range(len(matrix))]
print(f'主对角线: {main_diag}')  # [1, 5, 9]

anti_diag = [matrix[i][len(matrix) - 1 - i] for i in range(len(matrix))]
print(f'反对角线: {anti_diag}')  # [3, 5, 7]

# 获取某一行或某一列的切片
# 取第2行(索引1)的前2个元素
print(matrix[1][:2])  # [4, 5]

# 取第1列的所有元素
col_0 = [row[0] for row in matrix]
print(col_0)  # [1, 4, 7]

# 获取子矩阵
def submatrix(matrix, row_start, row_end, col_start, col_end):
    """提取子矩阵"""
    return [row[col_start:col_end] for row in matrix[row_start:row_end]]

print(submatrix(matrix, 0, 2, 1, 3))  # [[2, 3], [5, 6]]

四、二维列表的修改

4.1 修改单个元素和整行

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 修改单个元素
matrix[1][2] = 99
print(matrix[1])  # [4, 5, 99]

# 修改整行
matrix[0] = [10, 20, 30]
print(matrix)  # [[10, 20, 30], [4, 5, 99], [7, 8, 9]]

# 修改整列(需要通过循环)
for row in matrix:
    row[1] = 0  # 将第2列全部设为0
print(matrix)  # [[10, 0, 30], [4, 0, 99], [7, 0, 9]]

4.2 增加和删除行列

matrix = [[1, 2, 3], [4, 5, 6]]

# 增加一行
matrix.append([7, 8, 9])
print(matrix)  # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 增加一列(给每行追加一个元素)
for i, row in enumerate(matrix):
    row.append((i + 1) * 10)
print(matrix)  # [[1, 2, 3, 10], [4, 5, 6, 20], [7, 8, 9, 30]]

# 删除一行
del matrix[1]
print(matrix)  # [[1, 2, 3, 10], [7, 8, 9, 30]]

# 删除一列
for row in matrix:
    del row[-1]  # 删除每行的最后一个元素
print(matrix)  # [[1, 2, 3], [7, 8, 9]]

# 插入一行
matrix.insert(1, [100, 200, 300])
print(matrix)  # [[1, 2, 3], [100, 200, 300], [7, 8, 9]]

五、矩阵运算实战

5.1 矩阵转置

def transpose(matrix):
    """转置矩阵——行列互换"""
    if not matrix:
        return []
    rows, cols = len(matrix), len(matrix[0])
    # 创建 cols × rows 的新矩阵
    return [[matrix[i][j] for i in range(rows)] for j in range(cols)]

matrix = [
    [1, 2, 3],
    [4, 5, 6],
]
transposed = transpose(matrix)
print('原矩阵:')
for row in matrix:
    print(row)
print('转置:')
for row in transposed:
    print(row)
# [1, 4]
# [2, 5]
# [3, 6]

# 更简单的方式:用zip
transposed2 = [list(col) for col in zip(*matrix)]
print(transposed2)  # [[1, 4], [2, 5], [3, 6]]

5.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(matrix, scalar):
    """矩阵数乘:每个元素乘以标量"""
    return [[cell * scalar for cell in row] for row in matrix]


a = [[1, 2, 3], [4, 5, 6]]
b = [[7, 8, 9], [1, 2, 3]]

print('矩阵加法:')
for row in matrix_add(a, b):
    print(row)
# [8, 10, 12]
# [5, 7, 9]

print('矩阵数乘:')
for row in matrix_scalar_mul(a, 2):
    print(row)
# [2, 4, 6]
# [8, 10, 12]

5.3 矩阵乘法

def matrix_multiply(a, b):
    """矩阵乘法: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


a = [[1, 2, 3], [4, 5, 6]]      # 2×3
b = [[7, 8], [9, 10], [11, 12]]  # 3×2

c = matrix_multiply(a, b)
print('矩阵乘法结果 (2×2):')
for row in c:
    print(row)
# [58, 64]
# [139, 154]

# 验证:c[0][0] = 1*7 + 2*9 + 3*11 = 7 + 18 + 33 = 58 ✓

六、棋盘游戏——二维列表的经典应用

6.1 井字棋(Tic-Tac-Toe)

class TicTacToe:
    def __init__(self):
        # 3×3的棋盘
        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

    def get_empty_cells(self):
        """获取所有空单元格的位置"""
        return [(i, j) for i in range(3) for j in range(3)
                if self.board[i][j] == ' ']

6.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  # -1表示地雷

    # 计算每个非雷格子的数字(周围8格的地雷数)
    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)
print('扫雷棋盘(-1=雷,数字=周围雷数):')
for row in board:
    print(' '.join(f'{cell:2d}' for cell in row))

七、数据表格处理

7.1 CSV到二维列表

def parse_csv_to_matrix(text, delimiter=','):
    """将CSV文本解析为二维列表"""
    lines = text.strip().split('\n')
    return [line.split(delimiter) for line in lines if line.strip()]


csv_text = '''
姓名,年龄,城市,职业
小明,25,北京,工程师
小红,23,上海,设计师
小刚,26,广州,分析师
'''

data = parse_csv_to_matrix(csv_text)
print('原始数据:')
for row in data:
    print(row)


# 按列排序
def sort_by_column(data, col_index, numeric=False):
    """按指定列排序二维列表"""
    headers = data[0]
    body = data[1:]
    if numeric:
        body.sort(key=lambda row: float(row[col_index]))
    else:
        body.sort(key=lambda row: row[col_index])
    return [headers] + body


sorted_data = sort_by_column(data, 1, numeric=True)
print('\n按年龄排序:')
for row in sorted_data:
    print(row)

7.2 数据透 视

def pivot_table(data, row_field, col_field, value_field, agg_func=sum):
    """
    用二维列表模拟数据透 视表
    data: 字典列表
    """
    # 收集所有唯一的行和列值
    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])
        if key not in groups:
            groups[key] = []
        groups[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
sales_data = [
    {'月份': '1月', '产品': 'A', '销售额': 100},
    {'月份': '1月', '产品': 'B', '销售额': 150},
    {'月份': '1月', '产品': 'A', '销售额': 200},
    {'月份': '2月', '产品': 'A', '销售额': 120},
    {'月份': '2月', '产品': 'B', '销售额': 180},
    {'月份': '3月', '产品': 'A', '销售额': 90},
    {'月份': '3月', '产品': 'B', '销售额': 160},
]
pivot, rows, cols = pivot_table(sales_data, '月份', '产品', '销售额', sum)
print('数据透 视表:')
print(f'{"":>6}', end='')
for c in cols:
    print(f'{c:>8}', end='')
print()
for i, r in enumerate(rows):
    print(f'{r:>6}', end='')
    for j in range(len(cols)):
        print(f'{pivot[i][j]:>8}', end='')
    print()

八、三维列表与更高维

8.1 三维列表基础

# 三维列表 = 多个二维平面的堆叠
# 例如:2个平面 × 3行 × 4列

cube = [
    [  # 平面0
        [1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12],
    ],
    [  # 平面1
        [13, 14, 15, 16],
        [17, 18, 19, 20],
        [21, 22, 23, 24],
    ],
]

# 访问:[平面][行][列]
print(cube[0][1][2])  # 7(平面0,行1,列2)
print(cube[1][2][3])  # 24(平面1,行2,列3)

# 遍历三维列表
for p, plane in enumerate(cube):
    print(f'平面 {p}:')
    for row in plane:
        print(f'  {row}')

# 三维列表应用:RGB图像
# shape: [通道][行][列] → [3][height][width]
# 通道0=R, 通道1=G, 通道2=B

8.2 创建三维列表(避免引用陷阱)

# 错误做法(引用共享!)
# cube = [[[0]*3]*4]*2  # 平面间、行间都会共享引用!

# 正确做法:嵌套推导式
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('平面0修改后:')
for plane in cube:
    for row in plane:
        print(row)
    print()
# 只有平面0的行1列2变成99,其他不受影响

九、嵌套列表的深拷贝与浅拷贝

9.1 浅拷贝的问题

import copy

original = [[1, 2], [3, 4]]

# 浅拷贝——只复制外层列表
shallow = original[:]
# shallow = original.copy()
# shallow = list(original)
# shallow = copy.copy(original)

# 修改外层——不影响原列表
shallow.append([5, 6])
print(original)  # [[1, 2], [3, 4]](不变)

# 修改内层——影响原列表!
shallow[0][0] = 999
print(original)  # [[999, 2], [3, 4]](变了!)
print(shallow)   # [[999, 2], [3, 4], [5, 6]]

9.2 深拷贝解决

original = [[1, 2], [3, 4]]

# 深拷贝——递归复制所有层级
deep = copy.deepcopy(original)

# 修改内层——不影响原列表
deep[0][0] = 999
print(original)  # [[1, 2], [3, 4]](完全不变)
print(deep)      # [[999, 2], [3, 4]]

# 手动深拷贝二维列表(如果不想导入copy模块)
def manual_deep_copy_2d(matrix):
    """手动深拷贝二维列表"""
    return [row[:] for row in matrix]

# 但这只适用于二维!三维以上还是用copy.deepcopy

十、本篇小结

嵌套列表(特别是二维列表)是处理结构化数据的基础:

  1. 创建永远用推导式创建嵌套列表,不用乘法([[0]*n]*m会共享内层引用)
  2. 访问matrix[行][列],支持负索引和切片
  3. 修改:修改单个元素直接索引赋值,修改整列需要循环
  4. 遍历enumerate双重循环是最Pythonic的方式
  5. 矩阵运算:转置(zip或推导式)、加法、乘法是数据科学的基础操作
  6. 深拷贝:嵌套列表复制时注意浅拷贝只复制外层,内层对象共享

嵌套列表是连接"简单数据结构"和"实际应用"的桥梁。从井字棋到数据透 视表,从扫雷到图像处理——二维列表的应用无处不在。理解并避开引用共享的陷阱,是掌握嵌套列表的关键。

以上就是Python基础指南之二维列表(列表嵌套)的操作技巧的详细内容,更多关于Python二维列表操作的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文