Skip to content

第 1 章 认识 NumPy

学习目标

  • 知道 NumPy 是什么、解决什么问题
  • 完成 NumPy 的安装并验证版本
  • 会用 import numpy as np 导入并创建第一个数组
  • 理解 ndarray 的三个基本属性:维数、形状、数据类型
  • 理解「向量化」:数组运算作用于每个元素,不需要写循环

1.1 NumPy 是什么

NumPy(Numerical Python)是 Python 生态中最基础的科学计算库,核心是 ndarray(n 维数组)对象——一个存储同类型元素、支持整体运算的多维容器。

它解决一个具体问题:Python 列表做数值计算太慢、太啰嗦。比如把 100 万个数字每个加 1,用列表要写循环:

python
# 列表写法:必须逐个元素处理
nums = list(range(1_000_000))
result = [x + 1 for x in nums]

用 NumPy 数组,一次运算作用于整个数组:

python
import numpy as np
nums = np.arange(1_000_000)
result = nums + 1

nums + 1 内部用编译好的 C 代码批量执行,速度比列表循环快几十倍,代码也更短。这种「对整块数据做运算,而不是逐个元素循环」的方式,叫向量化(vectorization)。

本书所有示例基于 NumPy 2.x(写作时最新为 2.5.1)。代码统一在交互环境(REPL)或脚本文件中运行,约定运行方式用 uv run python(见 1.2)。

1.2 安装 NumPy

1.2.1 用 uv 安装(推荐)

本书配套仓库使用 uv 管理 Python 环境。在项目根目录:

$ uv sync

完成后验证:

$ uv run python -c "import numpy; print(numpy.__version__)"
2.5.1

1.2.2 用 pip 安装

如果你已有自己的 Python 环境,直接:

$ pip install numpy
$ python3 -c "import numpy; print(numpy.__version__)"

输出 2.x.x 即安装成功。后续所有示例,把 uv run python 换成你自己的 python3 即可。

1.3 导入 NumPy 与创建第一个数组

导入是用 import 语句把库加载进当前程序。NumPy 社区约定统一简写为 np:

python
import numpy as np

数组np.array 从列表创建。np.array(列表) 把列表转换成一个 ndarray:

python
import numpy as np

scores = np.array([85, 92, 78, 90, 88])
print(scores)

输出:

[85 92 78 90 88]

注意输出与列表的区别:数组打印时元素之间没有逗号,[85 92 78 90 88]

数组可以和数直接做算术,结果仍是数组:

python
print(scores + 10)     # 每个分数加 10
print(scores * 2)      # 每个分数乘 2

输出:

[95 102 88 100 98]
[170 184 156 180 176]

+* 逐元素作用于整个数组,这就是 1.1 说的向量化。对比列表:列表的 + 是拼接,* 是重复——语义完全不同,这是新手最容易混淆的地方。

1.4 数组的三个基本属性

每个 ndarray 都有三个必知属性:ndim(维数)、shape(形状)、dtype(数据类型)。

维数(ndim)是数组有几层方括号,一维是向量,二维是矩阵。

形状(shape)是一个元组,每个元素是该维度上的长度。

数据类型(dtype)是数组里元素的类型。ndarray 要求所有元素类型一致。

python
import numpy as np

a = np.array([1, 2, 3])          # 一维
b = np.array([[1, 2], [3, 4]])   # 二维

print("a:", a.ndim, a.shape, a.dtype)
print("b:", b.ndim, b.shape, b.dtype)

输出:

a: 1 (3,) int64
b: 2 (2, 2) int64

逐项解读:

  • a.ndim1b.ndim2:分别是一维、二维。
  • a.shape(3,):长度为 3 的一维数组(注意只有一个元素的元组要写逗号)。
  • b.shape(2, 2):2 行 2 列。
  • dtypeint64:64 位整数。元素里只要有一个小数,整个数组就是浮点型:
python
import numpy as np

c = np.array([1.5, 2.5])
print(c.dtype)

输出:

float64

也可以在创建时用 dtype 参数指定类型:

python
import numpy as np

d = np.array([1, 2, 3], dtype=np.float64)
print(d)
print(d.dtype)

输出:

[1. 2. 3.]
float64

注意打印时浮点数组显示为 1. 2. 3.——每个数带一个小数点,这是浮点数的标准显示格式。

1.5 二维数组的打印格式

二维数组打印成矩阵形状,自动换行对齐:

python
import numpy as np

grades = np.array([[85, 90], [78, 88], [92, 95]])
print(grades)
print("形状:", grades.shape)

输出:

[[85 90]
 [78 88]
 [92 95]]
形状: (3, 2)

形状 (3, 2) 读作「3 行 2 列」:3 个学生,每人 2 门成绩。这个「先学一个量纲,再学怎么操作它」的顺序,贯穿全书。

1.6 数组的简单统计

ndarray 自带常用的统计方法,无需循环:

python
import numpy as np

scores = np.array([85, 92, 78, 90, 88])
print("总和:", scores.sum())
print("平均:", scores.mean())
print("最大:", scores.max())
print("最小:", scores.min())
print("个数:", scores.size)

输出:

总和: 433
平均: 86.6
最大: 92
最小: 78
个数: 5

sum 是求和方法,mean 求平均,max/min 求最大/最小,size 是元素总数。这些方法在二维数组上默认作用于全部元素,「按轴统计」在第 6 章展开。

1.7 读懂第一条报错

NumPy 报错和 Python 报错格式一样,是 traceback。常见的第一条报错是形状不匹配

在交互环境中输入:

>>> import numpy as np
>>> np.array([1, 2, 3]) + np.array([1, 2])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (3,) (2,)

解读:错误类型是 ValueError(值错误),信息是 operands could not be broadcast together with shapes (3,) (2,)(操作数无法按形状 (3,) 和 (2,) 进行广播)。两个数组长度不同(3 和 2),不能逐元素相加。「广播」是第 7 章的主题,现在只需记住:两个形状不兼容的数组做运算会报 ValueError

读 NumPy 报错三步:

  1. 最后一行:错误类型 + 一句话说明,形状错误的信息里一定带有 shapes (...)
  2. 中间的行:出错的是哪条语句、哪个文件。
  3. 核对参与运算的数组形状:用 print(数组.shape) 确认。

动手实践

  1. 在项目根目录运行 uv run python 进入交互环境,验证 numpy.__version__
  2. 创建你自己的身高数组 np.array([...]),打印它,再打印 ndimshapedtype
  3. 把数组每个元素减 5 并打印,验证结果仍是数组;再用 Python 列表做同样的 - 5,观察报错——这直观展示了数组与列表运算语义的区别。
  4. 故意让 np.array([1, 2, 3])np.array([1, 2]) 相加,用「读报错三步」说出:错误类型是什么、两个操作数的形状各是什么。

常见错误

错误写法现象原因
np.array(1, 2, 3)TypeError: array() takes from 1 to 2 positional argumentsarray 只接受一个序列参数,应写成 np.array([1, 2, 3])
[1, 2, 3] + [4, 5, 6]结果是 [1, 2, 3, 4, 5, 6]列表的 + 是拼接,不是逐元素相加;要用 np.array
np.array([1, 2, 3]) + np.array([1, 2])ValueError: operands could not be broadcast...形状不兼容,不能逐元素运算
import numpy 后直接用 array(...)NameError: name 'array' is not defined导入简写后要用 np.array(...);或写成 from numpy import array
numpy.array([1, 2])(大写 N)ModuleNotFoundError: No module named 'numpy'包名是小写 numpy;先确认已 pip install numpy

章末练习

基础

  1. 写一段代码:创建数组 [10, 20, 30, 40, 50],分别打印它的 ndimshapedtype
  2. 创建二维数组 [[1, 2, 3], [4, 5, 6]],打印形状并说明它代表几行几列。
  3. 验证安装:用一行命令输出你本机 NumPy 的版本号。

提高

  1. 用列表 [1, 2, 3] 分别做 [1, 2, 3] * 2np.array([1, 2, 3]) * 2,打印两个结果,并解释为什么不同。
  2. 创建数组 np.array([1.0, 2, 3])(混入一个小数),打印它的 dtype,再解释为什么整个数组变成浮点型。

挑战

  1. 不运行代码,预测 np.array([1, 2, 3]).mean() 的输出,再运行验证;然后用列表完成同样任务(需要循环或 sum(lst)/len(lst)),比较两种写法。
  2. np.arange(1_000_000) 创建 100 万个数,执行 +1 运算;用列表推导式完成同样的 +1,用 time 模块分别计时,感受向量化的速度差异(提示:列表版本大约慢几十倍)。

章末自测

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. NumPy 的核心对象是什么?
    • A. list
    • B. ndarray
    • C. dict
    • D. Series
  2. NumPy 社区约定的导入写法是?
    • A. import numpy
    • B. import numpy as np
    • C. from numpy import *
    • D. import np
  3. np.array([1, 2, 3]).shape 的结果是?
    • A. 3
    • B. (3,)
    • C. (1, 3)
    • D. [3]
  4. np.array([1.5, 2]).dtype 的结果是?
    • A. int64
    • B. float64
    • C. object
    • D. str
  5. np.array([[1, 2], [3, 4]])ndim 是?
    • A. 1
    • B. 2
    • C. 4
    • D. (2, 2)
  6. np.array([1, 2, 3]) + 10 的结果是?
    • A. [11, 12, 13](列表)
    • B. [1, 2, 3, 10]
    • C. [11 12 13](数组)
    • D. 报错
  7. 数组与列表的一个关键区别是?
    • A. 数组可以存放不同类型
    • B. 数组要求元素类型一致,且运算向量化
    • C. 列表支持索引,数组不支持
    • D. 没有区别
  8. np.array([1, 2, 3]) + np.array([1, 2]) 会?
    • A. 返回 [2, 4, 3]
    • B. 返回 [2, 4]
    • C. 报 ValueError
    • D. 返回列表
  9. 下列哪个是查看数组元素个数的属性?
    • A. len(方法)
    • B. shape
    • C. ndim
    • D. size
  10. 关于向量化,正确的说法是?
    • A. 必须用 for 循环处理数组
    • B. 一次运算作用于整个数组,内部用编译代码批量执行
    • C. 只有列表才支持向量化
    • D. 向量化会让程序更慢