不用NumPy怎么用Python类实现矩阵运算

码头散步
码头散步
发布时间:2026-11-07| 4人浏览过
原创

本文介绍如何脱离NumPy库,使用纯Python二维列表和面向对象类封装实现矩阵的初始化、加法、标量乘法、矩阵乘法与转置运算。

脱离第三方数学计算库时,用Python原生语言实现矩阵运算的核心方案是:以二维嵌套列表(list[list])作为基础内存容器,利用面向对象类封装其行列维度,并通过重载Python运算符特殊方法(如 __add__、__mul__、__matmul__)来提供直观的矩阵代数语法。

一、数据结构选型与设计边界

Python标准库并没有直接提供类似 ndarray 的多维连续内存数组。最直接且易维护的底层载体是嵌套列表,即“外层列表代表行,内层列表代表列”。

在使用原生列表封装矩阵时,必须注意 Python 引用的浅拷贝问题。如果使用类似 [[0] * c] * r 的写法,外层列表存储的实际上是指向同一个内层列表的引用,修改一个元素会导致整列被联动修改。正确的做法是通过列表推导式独立创建每一行。

不用NumPy怎么用Python类实现矩阵运算
图 1:原生二维列表结构向 Matrix 面向对象类的映射

二、基础类构建与初始化检验

初始化矩阵对象时,不能简单保存传入的二维数组,需要校验传入数据的规整性:外层必须是非空列表,每行的列数必须一致,且内部元素必须支持数值计算。

class Matrix:
    def __init__(self, data):
        # 校验外层是否为非空列表
        if not isinstance(data, list) or len(data) == 0:
            raise ValueError("矩阵数据必须为非空二维列表")
        
        # 校验第一行是否为列表
        if not isinstance(data[0], list) or len(data[0]) == 0:
            raise ValueError("矩阵行数据必须为非空列表")
        
        self.rows = len(data)
        self.cols = len(data[0])
        
        # 深度复制并检查每一行的列数是否一致
        self.data = []
        for row in data:
            if not isinstance(row, list) or len(row) != self.cols:
                raise ValueError("所有行的列数必须保持严格一致")
            self.data.append([float(x) for x in row])

    @property
    def shape(self):
        return (self.rows, self.cols)

    def __repr__(self):
        # 格式化输出方便在终端调试观察
        lines = ["[" + ", ".join(f"{val:8.2f}" for val in row) + "]" for row in self.data]
        return "Matrix([\n  " + ",\n  ".join(lines) + "\n])"
注意边界条件:通过 float(x) 强制类型转换,一方面保证了整型和浮点型均可参与统一的数学计算,另一方面排除了非数值类型输入。使用推导式重建列表也避免了外部修改原列表影响类内部状态。

三、矩阵加法与标量乘法实现

矩阵加法的先决条件是两矩阵维度完全一致。加法是对应位置元素相加(Element-wise Addition)。我们通过实现 Python 的 __add__ 魔法方法来启用 + 操作符。

数乘运算(标量乘法)中,标量与矩阵内的每个数值相乘。通常我们希望同时支持 matrix * 2 和 2 * matrix,这就需要一并重载 __mul__ 与 __rmul__。

不用NumPy怎么用Python类实现矩阵运算
图 2:矩阵加法运算的流程与校验机制
    def __add__(self, other):
        if not isinstance(other, Matrix):
            raise TypeError("加法操作只支持两个 Matrix 对象之间进行")
        if self.shape != other.shape:
            raise ValueError(f"矩阵维度不兼容,无法相加: {self.shape} vs {other.shape}")
        
        result = [
            [self.data[i][j] + other.data[i][j] for j in range(self.cols)]
            for i in range(self.rows)
        ]
        return Matrix(result)

    def __mul__(self, scalar):
        # 实现标量数乘
        if not isinstance(scalar, (int, float)):
            raise TypeError("标量乘法仅支持与数字进行")
        
        result = [
            [self.data[i][j] * scalar for j in range(self.cols)]
            for i in range(self.rows)
        ]
        return Matrix(result)

    def __rmul__(self, scalar):
        # 允许标量位于左侧,如 3 * A
        return self.__mul__(scalar)

四、核心算法:矩阵乘法与转置

在严格的代数定义中,矩阵乘法(Matrix Multiplication)并非对应元素相乘,而是行与列的点积。设矩阵 $A$ 维度为 $m \times k$,$B$ 维度为 $k \times n$,则乘积结果矩阵 $C$ 维度为 $m \times n$。

Python 3.5 引入了专门用于矩阵乘法的符号 @,对应魔法方法为 __matmul__。遵循约定,我们将点乘/矩阵积放在 __matmul__ 中实现,避免与数乘逻辑混淆。

不用NumPy怎么用Python类实现矩阵运算
图 3:矩阵乘法行向量与列向量的点积映射机制
    def __matmul__(self, other):
        if not isinstance(other, Matrix):
            raise TypeError("矩阵乘法运算必须在两个 Matrix 对象之间进行")
        
        # 校验维度: A 的列数必须等于 B 的行数
        if self.cols != other.rows:
            raise ValueError(f"维度不匹配,无法相乘: 左矩阵列数 {self.cols} != 右矩阵行数 {other.rows}")
        
        # 创建 m 行 n 列的全零临时矩阵
        result = [[0.0 for _ in range(other.cols)] for _ in range(self.rows)]
        
        # 三重循环计算内积
        for i in range(self.rows):
            for j in range(other.cols):
                total = 0.0
                for k in range(self.cols):
                    total += self.data[i][k] * other.data[k][j]
                result[i][j] = total
                
        return Matrix(result)

    def transpose(self):
        """转置矩阵:行列互换"""
        result = [
            [self.data[r][c] for r in range(self.rows)]
            for c in range(self.cols)
        ]
        return Matrix(result)

    @property
    def T(self):
        return self.transpose()

五、完整代码与执行验证

为了验证实现的有效性,我们使用一个可独立运行的代码块,执行矩阵加法、标量乘法、转置以及矩阵相乘。

if __name__ == "__main__":
    # 初始化两个 2x3 矩阵
    A = Matrix([
        [1, 2, 3],
        [4, 5, 6]
    ])
    
    B = Matrix([
        [7, 8, 9],
        [1, 2, 3]
    ])

    print("--- 矩阵 A ---")
    print(A)

    print("\n--- 矩阵加法 (A + B) ---")
    print(A + B)

    print("\n--- 标量乘法 (A * 2) ---")
    print(A * 2)

    print("\n--- 矩阵转置 (A.T) ---")
    print(A.T)

    print("\n--- 矩阵乘法 (A @ A.T) ---")
    # A 为 2x3, A.T 为 3x2, 相乘结果应为 2x2
    print(A @ A.T)
终端输出示例
--- 矩阵 A --- Matrix([ [ 1.00, 2.00, 3.00], [ 4.00, 5.00, 6.00] ]) --- 矩阵加法 (A + B) --- Matrix([ [ 8.00, 10.00, 12.00], [ 5.00, 7.00, 9.00] ]) --- 标量乘法 (A * 2) --- Matrix([ [ 2.00, 4.00, 6.00], [ 8.00, 10.00, 12.00] ]) --- 矩阵转置 (A.T) --- Matrix([ [ 1.00, 4.00], [ 2.00, 5.00], [ 3.00, 6.00] ]) --- 矩阵乘法 (A @ A.T) --- Matrix([ [ 14.00, 32.00], [ 32.00, 77.00] ])

六、原生实现的性能特征与适用边界

完全采用 Python 原生类实现矩阵运算,代码直观、无第三方依赖、便于深入理解底层数学规则。但在生产环境或大规模数据场景下,需要清楚其客观性能限制。

不用NumPy怎么用Python类实现矩阵运算
图 4:原生类封装与专业数值计算库的技术定位对比

原生实现的矩阵乘法复杂度为 $O(n^3)$,受限于解释器开销与列表内存布局的不连续性,当矩阵维度上升到数百级别时耗时会显著上升。但这套封装对于不便于引入庞大依赖库(如受限的嵌入式环境、云函数冷启动限制环境)、基础教学以及纯算法验证来说,已经提供了足够完备且符合数学直觉的运算能力。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。