
import numpy as np
N, M, P = map(int, input().split())
print(np.zeros((N, M, P), dtype=int))
print(np.ones((N, M, P), dtype=int))
为什么 numpy.zeros((N, M, P)) 里面要写“双层括号”
这个问题本质上是在问:
为什么不是 numpy.zeros(N, M, P),而是 numpy.zeros((N, M, P))?
关键原因只有一句话:
zeros() 需要的不是 3 个分开的数字,而是 1 个“形状 shape 参数”。这个 shape 参数本身要写成一个元组。
先看最外层括号和里层括号分别在干什么
这句代码:
numpy.zeros((N, M, P))
其实可以拆成两层来看。
第一层括号:函数调用的括号
最外面这一层:
zeros(...)
表示“调用函数”。
也就是说,你在调用 numpy.zeros 这个函数,并把某个参数传进去。
第二层括号:元组 tuple 的括号
里面这一层:
(N, M, P)
表示一个元组,也就是一个“形状说明”。
它告诉 NumPy:
“我要创建一个 3 维数组,它的形状是 N x M x P。”
所以:
numpy.zeros((N, M, P))
真正的意思是:
“调用 zeros 函数,并把 (N, M, P) 这个形状作为一个整体传进去。”
为什么不能写成 numpy.zeros(N, M, P)
因为这样写时,Python 会把它理解成:
numpy.zeros(第1个参数, 第2个参数, 第3个参数)
也就是说,你传了 3 个独立参数。
但 numpy.zeros() 的第一个核心参数应该是:
shape
它希望拿到的是一个整体的形状,而不是 3 个散开的数字。
所以二维、三维这种情况,一般都要写成元组:
numpy.zeros((2, 3))
numpy.zeros((2, 3, 4))
numpy.ones((4, 5))
那为什么一维数组有时候不用双层括号
比如你会看到:
numpy.zeros(5)
这也能运行。
因为一维数组的 shape 可以直接写成一个整数 5,表示长度为 5。
也就是说:
numpy.zeros(5)
相当于创建:
[0. 0. 0. 0. 0.]
但如果是二维、三维,就必须把形状整体写出来:
numpy.zeros((2, 3))
numpy.zeros((2, 3, 4))
所以你可以这样记:
一维
shape 可以直接写一个数
numpy.zeros(5)
二维及以上
shape 通常写成元组
numpy.zeros((2, 3))
numpy.zeros((2, 3, 4))
你可以把它理解成“地址”或者“规格说明”
比如你去定做一个盒子:
- 长 2
- 宽 3
- 高 4
这三个数字必须作为“一组规格”一起交给工厂。
不能今天给个 2,明天给个 3,后天给个 4。
NumPy 这里也是一样:
(2, 3, 4)
就是这一组规格。
dtype=int 到底是什么意思
dtype 是:
data type
也就是“数据类型”。
它是在告诉 NumPy:
这个数组里的元素,要用什么类型来保存。
例如:
numpy.zeros((2, 2), dtype=int)
意思是:
“创建一个 2×2 的全 0 数组,并且这些 0 要按整数类型存储。”
为什么不写 dtype=int 时会输出浮点数
因为 numpy.zeros() 和 numpy.ones() 的默认类型通常是浮点数,也就是 float。
例如:
import numpy as np
print(np.zeros((2, 2)))
输出一般会是:
[[0. 0.]
[0. 0.]]
你会看到是 0.,不是 0。
这个 0. 表示它是浮点数。
同理:
print(np.ones((2, 2)))
输出一般是:
[[1. 1.]
[1. 1.]]
加上 dtype=int 后会发生什么
import numpy as np
print(np.zeros((2, 2), dtype=int))
print(np.ones((2, 2), dtype=int))
输出会变成:
[[0 0]
[0 0]]
[[1 1]
[1 1]]
这时候就不再显示小数点了,因为元素类型变成了整数。
dtype 常见可以改成哪些类型
下面是最常见的几种。
1. dtype=int
整数类型
import numpy as np
print(np.zeros((2, 3), dtype=int))
print(np.ones((2, 3), dtype=int))
输出:
[[0 0 0]
[0 0 0]]
[[1 1 1]
[1 1 1]]
2. dtype=float
浮点数类型
import numpy as np
print(np.zeros((2, 3), dtype=float))
print(np.ones((2, 3), dtype=float))
输出:
[[0. 0. 0.]
[0. 0. 0.]]
[[1. 1. 1.]
[1. 1. 1.]]
这其实就是默认情况。
3. dtype=bool
布尔类型
import numpy as np
print(np.zeros((2, 3), dtype=bool))
print(np.ones((2, 3), dtype=bool))
输出:
[[False False False]
[False False False]]
[[ True True True]
[ True True True]]
因为在布尔世界里:
- 0 对应
False - 1 对应
True
4. dtype=complex
复数类型
import numpy as np
print(np.zeros((2, 2), dtype=complex))
print(np.ones((2, 2), dtype=complex))
输出通常类似:
[[0.+0.j 0.+0.j]
[0.+0.j 0.+0.j]]
[[1.+0.j 1.+0.j]
[1.+0.j 1.+0.j]]
这个在初学阶段不常用,但你知道它存在就行。
5. 更精确的类型写法
你还会看到这些:
dtype=np.int32
dtype=np.int64
dtype=np.float32
dtype=np.float64
这表示更具体的类型。
比如:
import numpy as np
a = np.zeros((2, 2), dtype=np.int32)
b = np.ones((2, 2), dtype=np.float64)
它们和简单写 int、float 的区别在于:
占用多少字节、精度多高,是更细的控制。
初学阶段先会用:
dtype=int
dtype=float
dtype=bool
就够了。
那 zeros 和 ones 这两个函数“还有哪些输出”
这里你要分清一件事:
zeros() 和 ones() 决定的是“值”
zeros()生成全 0ones()生成全 1
dtype 决定的是“这些值用什么类型显示和存储”
所以你看到的输出变化,其实主要来自 dtype。
比如同样是“全 1”:
整数版
np.ones((2, 2), dtype=int)
输出:
[[1 1]
[1 1]]
浮点版
np.ones((2, 2), dtype=float)
输出:
[[1. 1.]
[1. 1.]]
布尔版
np.ones((2, 2), dtype=bool)
输出:
[[ True True]
[ True True]]
也就是说,值本质上还是“1”,只是显示形式和存储方式不同。
这个函数还能怎么修改
你现在最需要掌握的修改点有两个:
第一,改形状 shape
比如:
np.zeros(5)
np.zeros((2, 3))
np.zeros((2, 3, 4))
这会生成不同维度、不同大小的数组。
第二,改数据类型 dtype
比如:
np.zeros((2, 2), dtype=int)
np.zeros((2, 2), dtype=float)
np.zeros((2, 2), dtype=bool)
这样可以改变输出样子和内部存储类型。
一个最清楚的对比示例
你可以直接看这一组:
import numpy as np
print(np.zeros((2, 3)))
print(np.zeros((2, 3), dtype=int))
print(np.zeros((2, 3), dtype=bool))
print(np.ones((2, 3)))
print(np.ones((2, 3), dtype=int))
print(np.ones((2, 3), dtype=bool))
你会发现:
- 默认是浮点数
dtype=int变整数dtype=bool变布尔值
初学者最容易混淆的点
第一种混淆:把双层括号看成“多余括号”
其实不是多余,而是:
- 外层:函数调用
- 内层:shape 元组
第二种混淆:以为 zeros 只能生成整数 0
不是。
zeros 生成的是“数值为 0 的数组”,但这个 0 可以是:
- 整数 0
- 浮点数 0.
- 布尔值 False
- 复数 0+0j
要看 dtype 怎么定。
第三种混淆:以为 dtype=int 是“把形状变成整数”
不是。
dtype=int 改的是数组里元素的类型,不是数组的大小,不是数组的维度。
比如:
np.zeros((2, 3), dtype=int)
这里:
(2, 3)是形状int是元素类型
这两件事完全不同。
本节小结
你可以把这道题的核心记成三句话。
1. zeros() 和 ones() 需要一个 shape
shape 表示数组的形状。
一维可以写:
np.zeros(5)
二维、三维通常写:
np.zeros((2, 3))
np.zeros((2, 3, 4))
2. 双层括号不是重复
np.zeros((N, M, P))
- 外层括号:调用函数
- 内层括号:写元组,表示形状
3. dtype 决定元素类型
dtype=int
dtype=float
dtype=bool
它决定你看到的是:
0/10./1.False/True
一个小练习
你可以自己先试着判断下面每句代码会输出成什么类型:
import numpy as np
print(np.zeros((2, 2)))
print(np.zeros((2, 2), dtype=int))
print(np.ones((2, 2), dtype=bool))
提示你只判断“输出里会看到什么风格”:
- 是
0.还是0 - 是
1还是True
为什么 numpy.zeros(shape) 里的 shape 常写成元组,而不是普通整数
这一节可以专门作为你前面那道题的补充说明。很多初学者一开始都会把这里看得有点模糊:一会儿看到 numpy.zeros(3),一会儿又看到 numpy.zeros((3, 2)),再加上题目里还常写成 shape = tuple(map(int, input().split())),就很容易混在一起。
其实这里真正要弄明白的是:shape 不是某一个数字本身,而是“数组形状的描述”。
先说结论:shape 不一定非得是元组
这句话要先说清楚,不然容易记错。
numpy.zeros(shape) 里的 shape:
- 可以是一个普通整数
- 也可以是一个元组
比如下面两种写法都合法:
import numpy as np
print(np.zeros(3))
print(np.zeros((3, 2)))
所以严格来说,不是“必须是元组”。
更准确的说法应该是:
当你要表示多维数组时,shape 通常要写成元组;当你只表示一维数组时,写普通整数也可以。
shape 到底是什么
shape 可以理解成:
数组每一维有多大。
比如:
(3,)
表示一维数组,有 3 个元素。
(3, 2)
表示二维数组,有 3 行 2 列。
(3, 3, 3)
表示三维数组,外层有 3 块,每块里有 3 行,每行有 3 个元素。
所以 shape 不是“某一个数字”,而是“这个数组长什么样”。
为什么普通整数只能自然表示一维
我们先看这个例子:
import numpy as np
print(np.zeros(3))
这里的 3 表示:
“请给我一个长度为 3 的一维数组。”
输出大致是:
[0. 0. 0.]
这很好理解,因为一维数组只需要一个数字就够了。
你可以把它想成:
- 一维数组:只要说长度是多少
- 二维数组:要说行和列分别是多少
- 三维数组:要说每一层、每一行、每一列分别是多少
所以:
- 一维时,一个整数就够
- 多维时,一个整数不够,必须同时描述多个维度的大小
这就是为什么二维、三维、四维时,通常要写元组。
为什么多维要用元组来描述
看下面几个例子:
import numpy as np
print(np.zeros((3, 2)))
print(np.zeros((2, 2, 2)))
这里的 (3, 2) 和 (2, 2, 2),本质上是在告诉 NumPy:
- 第一维多大
- 第二维多大
- 第三维多大
- ……
一个整数只能表示一个值,而元组可以一次装下多个值。
所以:
np.zeros(3)
只是在说:
“做一个长度为 3 的一维数组。”
而:
np.zeros((3, 2))
是在说:
“做一个 3 行 2 列的二维数组。”
而:
np.zeros((3, 2, 2, 2))
是在说:
“做一个四维数组,每一维大小分别是 3、2、2、2。”
为什么题目里更适合统一写成元组
像你做的这道题,输入可能是:
3 2
也可能是:
3 3 3
还可能是:
3 2 2 2
这就说明:
维度个数不固定。
既然维度个数不固定,那最合适的办法就不是写死成:
N, M, P = ...
而是把整行输入全部收起来,统一变成一个“形状描述”。
这时候元组就特别合适。
例如:
- 输入
3 2,变成(3, 2) - 输入
3 3 3,变成(3, 3, 3) - 输入
3 2 2 2,变成(3, 2, 2, 2)
然后统一交给:
np.zeros(shape)
np.ones(shape)
这样程序就不需要提前知道到底是二维、三维还是四维了。
一个很容易混淆的点:3 和 (3,) 的关系
这个地方非常值得单独讲清楚。
在 NumPy 里:
np.zeros(3)
和
np.zeros((3,))
效果是一样的,都是创建长度为 3 的一维数组。
因为:
3表示一维长度为 3(3,)表示形状是一个只有 1 个维度的元组,这个维度大小是 3
也就是说,一维时,这两种写法都可以。
但是注意一个很关键的 Python 语法点:
(3)
这不是元组,这只是整数 3。
真正的单元素元组要写成:
(3,)
后面的逗号不能少。
这是初学者非常容易踩的坑。
你可以记一句:
不是括号让它变成元组,而是逗号让它变成元组。
为什么这道题里不直接用整数,而要用 tuple(...)
因为这道题不是固定一维。
如果你只写:
shape = int(input())
那只能接收一个数字,比如 3。
可题目给的是一整行空格分隔的数字:
3 2
或者:
3 3 3
这显然不是一个单独整数,而是一组维度信息。
所以必须把这一组数字收集起来。
而收集成元组,就是最自然、最规范的写法。
shape = tuple(map(int, input().split())) 这一整串到底在做什么
这句代码看起来长,但其实就是四步连起来写了。
shape = tuple(map(int, input().split()))
我们一点点拆开看。
第一步:input()
input()
作用是从键盘读入一整行内容。
假设用户输入的是:
3 2 2 2
那么 input() 读到的是一个字符串:
'3 2 2 2'
注意,它现在还是字符串,不是数字。
第二步:.split()
input().split()
作用是按空格切开。
于是:
'3 2 2 2'.split()
会变成:
['3', '2', '2', '2']
这里得到的是一个字符串列表。
也就是说,里面每一项还是字符串。
不是整数。
第三步:map(int, ...)
map(int, input().split())
这一部分的作用是:
把前面列表里的每个字符串,逐个转成整数。
也就是:
'3'变成3'2'变成2'2'变成2'2'变成2
所以逻辑上就相当于得到了:
3, 2, 2, 2
这里你可以先把 map 理解成:
“让 int() 对每个元素都做一次转换。”
第四步:tuple(...)
tuple(map(int, input().split()))
最后这一步是把前面那些转换后的整数收集成一个元组。
于是就得到:
(3, 2, 2, 2)
这个元组就正好可以作为 shape 传给 NumPy。
把这句完整地翻译成人话
shape = tuple(map(int, input().split()))
可以翻译成一句非常直白的话:
把用户输入的一整行数字按空格拆开,把每一项从字符串变成整数,再整体收成一个元组,作为数组的形状。
用几个例子彻底看懂这句代码
例 1:输入是 3 2
执行过程是:
input() -> '3 2'
split() -> ['3', '2']
map(int, ...) -> 3, 2
tuple(...) -> (3, 2)
最终:
shape = (3, 2)
例 2:输入是 3 3 3
执行过程是:
input() -> '3 3 3'
split() -> ['3', '3', '3']
map(int, ...) -> 3, 3, 3
tuple(...) -> (3, 3, 3)
最终:
shape = (3, 3, 3)
例 3:输入是 3 2 2 2
执行过程是:
input() -> '3 2 2 2'
split() -> ['3', '2', '2', '2']
map(int, ...) -> 3, 2, 2, 2
tuple(...) -> (3, 2, 2, 2)
最终:
shape = (3, 2, 2, 2)
为什么不用列表,而常用元组
这个地方也可以顺手补充一下。
其实 NumPy 在很多情况下也能接受列表,比如:
np.zeros([3, 2])
很多时候也能运行。
但在“表示 shape”这件事上,元组更常见,原因主要有两个。
第一,元组更像“固定的结构说明”。
shape 一般只是拿来描述数组形状,不是后面要频繁修改的数据,所以元组很合适。
第二,教材、官方示例、题目答案里,几乎都习惯用元组。
所以你跟着这个习惯写,最稳。
对于初学阶段,你可以先记成:
shape 的标准写法优先用元组。
这道题的最终标准写法
所以像这道题,最推荐写成:
import numpy as np
shape = tuple(map(int, input().split()))
print(np.zeros(shape, dtype=int))
print(np.ones(shape, dtype=int))
这里的思路非常统一:
先把输入处理成一个形状元组,再把这个形状元组交给 zeros 和 ones。
你以后怎么判断该用整数还是元组
你可以用一个很简单的判断办法。
如果你想创建的是一维数组,只需要一个长度,那么整数就够了:
np.zeros(5)
如果你想创建的是二维及以上数组,或者题目输入的维度个数不固定,就应该用元组:
np.zeros((3, 2))
np.zeros((3, 3, 3))
np.zeros((3, 2, 2, 2))
尤其是像题目这种“输入一整行维度”的情况,几乎就是在提示你应该写:
shape = tuple(map(int, input().split()))
本节小结
这一节最重要的有四句话。
第一,shape 不一定非得是元组。
一维数组时,普通整数也可以。
第二,多维数组通常要用元组。
因为一个整数只能表示一个维度,而元组可以同时表示多个维度。
第三,np.zeros(3) 和 np.zeros((3,)) 都表示一维长度为 3。
但 (3) 不是元组,(3,) 才是单元素元组。
第四,题目里写:
shape = tuple(map(int, input().split()))
本质上就是:
把整行输入的所有数字读进来,转成整数,再收成一个形状元组。
小练习
你可以先自己判断下面三句代码里,shape 最后分别是什么。
题目
shape = tuple(map(int, "4".split()))
shape = tuple(map(int, "2 3".split()))
shape = tuple(map(int, "2 1 3".split()))
提示
你可以按这个顺序去想:
先看 split() 后变成什么。
再看 map(int, ...) 后每个元素变成什么。
最后看 tuple(...) 收成什么。



