前言
先纠正标题里两个不严谨的地方。第一,NumPy 不是 Python 标准库,它是需要单独安装的第三方库(用pip install numpy);本机没装它,所以下面的调用无法在本机运行验证,API 签名均以 NumPy 官方文档为准,请你以自己的环境为准。第二,「矩阵运算函数」这个说法偏笼统——NumPy 里做矩阵运算的东西有三种形态:模块级函数(如numpy.dot)、ndarray的方法(如a.reshape(...))、以及运算符(@、*)。把a @ b叫「函数」并不准确,它是运算符,背后调用__matmul__。
另外要提前说清一个高频误解:在 NumPy 里,*是逐元素相乘(element-wise,也叫 Hadamard 积),不是矩阵乘法;矩阵乘法要用@运算符或numpy.matmul/numpy.dot。大量「矩阵乘法算错了」的 bug,根源就是把*当成了矩阵乘。
再补一句版本提醒:Python 2.7 已于 2020 年 1 月 1 日停止维护,较新的 NumPy 也早已不再支持 Python 2,网上老代码里的print语句(print x)、xrange等写法在 Python 3 里都不能用,本文示例一律按 Python 3 写。
本文按「建矩阵 → 改形状 → 做乘法 → 解线性方程组」的顺序,记几种常用的矩阵运算写法,并给出可对照官方的签名。
一、创建矩阵与查看形状
ndarray是 NumPy 的核心数据结构,二维的ndarray就是通常说的矩阵。
# 适用于 Python 3.8+ 且已安装 NumPy(以官方文档为准)
import numpy as np
A = np.array([[1, 2], [3, 4]]) # 由嵌套列表构造 2x2
Z = np.zeros((2, 3)) # 2 行 3 列,全 0,默认 float64
O = np.ones((3, 3)) # 3x3 全 1
I = np.eye(3) # 3x3 单位矩阵
print(A.shape) # (2, 2)
print(A.dtype) # int64(视平台而定)
print(Z.shape) # (2, 3)形状通过.shape属性读取,它是一个元组。修改形状不会复制数据,只要内存布局允许,.reshape返回的是视图(view)。
# 适用于 Python 3.8+ 且已安装 NumPy(以官方文档为准)
a = np.arange(6) # 一维 [0 1 2 3 4 5]
m = np.reshape(a, (2, 3)) # 变成 2x3
print(m.shape) # (2, 3)
print(a.shape) # (6,) ← 原数组不变二、转置:.T与numpy.transpose
转置有两种等价写法:数组属性.T,或函数numpy.transpose(a, axes=None)。
# 适用于 Python 3.8+ 且已安装 NumPy(以官方文档为准)
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A.shape) # (2, 3)
print(A.T.shape) # (3, 2)
print(np.transpose(A).shape) # (3, 2)官方文档特别指出:对一维数组,transpose返回的是原数组的一个未改动视图——一维没有「行列」可换。要留意多维数组的转置在三维及以上默认是「反转所有轴的顺序」,需要更精细控制时才传axes。
三、矩阵乘法:@、matmul、dot
三种写法:
| 写法 | 官方签名 / 说明 |
|---|
A @ B | 运算符,等价于调用__matmul__,Python 3.5 起支持(PEP 465) |
numpy.matmul(x1, x2, /, out=None, *, casting='same_kind', order='K', dtype=None, subok=True) | 函数形式,官方推荐 |
numpy.dot(a, b, out=None) | 老的通用函数,文档注明「using matmul or a @ b is preferred」 |
A * B | 逐元素相乘,不是矩阵乘 |
对两个二维数组,@、matmul、dot结果一致:
# 适用于 Python 3.8+ 且已安装 NumPy(以官方文档为准)
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A * B)
# [[ 5 12]
# [21 32]] ← 逐元素相乘
print(A @ B)
# [[19 22]
# [43 50]] ← 矩阵乘法
print(np.matmul(A, B)) # 与 A @ B 相同
print(np.dot(A, B)) # 二维时也与 A @ B 相同验算A @ B第一行第一列:1*5 + 2*7 = 19,对得上;A * B第一行第一列是1*5 = 5,也就是逐元素。
matmul和dot的差异在两处:一是matmul不接受标量,dot可以;二是维数大于 2 时,matmul把数组当成「一批矩阵」并按广播规则处理,dot则是把a的最后一个轴与b的倒数第二个轴求和。日常二维矩阵用哪个都行,但官方推荐matmul或@。
四、线性代数:numpy.linalg
矩阵求逆、行列式、解方程都在numpy.linalg里,签名如下(均以官方文档为准):
| 函数 | 签名 | 作用 |
|---|
numpy.linalg.inv | inv(a) | 求逆矩阵 |
numpy.linalg.det | det(a) | 求行列式 |
numpy.linalg.solve | solve(a, b) | 解线性方程组a @ x = b |
numpy.linalg.eig | eig(a) | 求特征值与特征向量 |
# 适用于 Python 3.8+ 且已安装 NumPy(以官方文档为准)
import numpy as np
A = np.array([[3.0, 1.0], [1.0, 2.0]])
print(np.linalg.det(A)) # 5.0 (3*2 - 1*1)
inv_A = np.linalg.inv(A)
print(np.allclose(A @ inv_A, np.eye(2))) # True ← 用 allclose 查浮点近似相等
b = np.array([9.0, 8.0])
x = np.linalg.solve(A, b)
print(x) # [2. 3.]
print(np.allclose(A @ x, b)) # True要点:
np.linalg.inv要求矩阵是方阵,否则抛LinAlgError;若矩阵奇异(不可逆),同样抛LinAlgError。- 解方程组优先用
np.linalg.solve(A, b),而不是先求逆再乘inv(A) @ b——后者数值上更不稳定,也更慢。 - 判断浮点结果是否相等不要用
==,用numpy.allclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)。
实战:解一个线性方程组
下面是一个完整的小例子:解A x = b,再回代验证。
# 适用于 Python 3.8+ 且已安装 NumPy(以官方文档为准)
import numpy as np
def solve_system(A, b):
"""解 A @ x = b,返回解向量;奇异或非方阵时抛 numpy.linalg.LinAlgError。"""
A = np.asarray(A, dtype=float)
b = np.asarray(b, dtype=float)
x = np.linalg.solve(A, b)
ok = np.allclose(A @ x, b)
return x, ok
if __name__ == "__main__":
A = [[2, 1, -1],
[-3, -1, 2],
[-2, 1, 2]]
b = [8, -11, -3]
x, ok = solve_system(A, b)
print("解:", x) # 解: [ 2. 3. -1.]
print("验证:", ok) # 验证: True逐行说明:
np.asarray(...)把列表转成数组,dtype=float保证后续按浮点运算,避免整数下整除问题。np.linalg.solve直接得到未知数向量x,没有显式求逆。np.allclose(A @ x, b)把结果代回验证,浮点误差在你的容差范围内就返回True。
常见坑点
- 把
*当矩阵乘法
❌C = A * B想做矩阵乘法 —— 得到的其实是逐元素积。 ✅ 矩阵乘用C = A @ B(或np.matmul(A, B))。
- 先求逆再乘 b 解方程
❌x = np.linalg.inv(A) @ b—— 数值稳定性差、更慢。 ✅x = np.linalg.solve(A, b)。
- 浮点结果用
==比较
❌if A @ x == b:—— 浮点误差导致偶尔为假。 ✅if np.allclose(A @ x, b):。
- 对非方阵求逆
❌ 对 2x3 矩阵调np.linalg.inv—— 抛numpy.linalg.LinAlgError。 ✅ 确认矩阵是方阵;非方阵要解方程用最小二乘之类的其他方法。
- 误以为
reshape会复制数据
❌b = a.reshape(2, 3)后改b,以为a不受影响 —— 若返回的是视图,两者共享底层数据。 ✅ 需要独立副本就显式b = a.reshape(2, 3).copy()。
- 用
numpy.matrix写线性代数
❌ 用np.matrix构造矩阵 —— 官方文档明确「It is no longer recommended to use this class, even for linear algebra. Instead use regular arrays.」,且该类未来可能被移除。 ✅ 用二维ndarray加@运算符。
- 在没装 NumPy 的环境里 import
❌import numpy后直接用 —— 未安装会ModuleNotFoundError: No module named 'numpy'。 ✅ 先pip install numpy,并在代码里用try/except ImportError给出友好提示(如果面向他人分发)。
- 整数数组下的除法
❌ 用整数类型的数组做平均,期望得到小数 —— 若走的是整除语义会截断。 ✅ 构造时指定dtype=float,或用/得到浮点结果(/在 Python 3 和 NumPy 里都是真除法,整除要写//)。
总结
| 目的 | 写法 | 备注 |
|---|
| 构造矩阵 | np.array、np.zeros、np.ones、np.eye | 二维ndarray |
| 形状 / 转置 | .shape、.reshape、.T、np.transpose | reshape常返回视图 |
| 矩阵乘法 | A @ B、np.matmul(A, B) | 官方推荐;np.dot亦可 |
| 逐元素乘 | A * B | 不是矩阵乘法 |
| 求逆 / 行列式 | np.linalg.inv、np.linalg.det | 需方阵,奇异抛LinAlgError |
| 解方程 | np.linalg.solve(A, b) | 优于先求逆 |
| 浮点比较 | np.allclose | 别用== |
NumPy 的矩阵运算记起来不复杂:乘法认准@,*只管逐元素;线性代数进numpy.linalg;结果比较用allclose;再避开np.matrix这个已被官方劝退的老类。由于 NumPy 是第三方库且版本演进较快,实际使用时请以你所用版本的官方文档为准。