np.zeros() creates a NumPy array of a given shape filled with zeros. Pass one number for a 1D array or a tuple for more dimensions, and add dtype if you don’t want floats:
import numpy as np
np.zeros(5) # [0. 0. 0. 0. 0.]
np.zeros((3, 2)) # 3 rows, 2 columns
np.zeros(4, dtype=int) # [0 0 0 0]
Below I cover the shape rules, dtypes, zeros_like(), how it compares with np.empty(), and the errors people hit. Everything ran on Python 3.14.7 and NumPy 2.5.3.

NumPy zeros() syntax and parameters
The full signature is np.zeros(shape, dtype=float, order='C', *, like=None). In practice you’ll set the first two:
shape: an int like5, or a tuple like(3, 2)for rows and columns.dtype: the element type. It defaults tofloat64, so zeros print as0..order:'C'stores rows together,'F'stores columns together. It only matters for speed with some libraries.like: lets array libraries that follow NumPy’s protocol return their own array type. You can ignore it for plain NumPy.
If import numpy fails, install it first with pip install numpy. The NumPy not found guide covers the usual install problems.
Create a 1D array of zeros with np.zeros
A single integer gives a one-dimensional array of that length:
import numpy as np
a = np.zeros(5)
print(a)
print("dtype:", a.dtype, "| shape:", a.shape, "| ndim:", a.ndim)
print(repr(a))
Output:
[0. 0. 0. 0. 0.]
dtype: float64 | shape: (5,) | ndim: 1
array([0., 0., 0., 0., 0.])
The dots in 0. mean these are floats, and dtype confirms float64. The shape (5,) is a one-item tuple, which is how NumPy writes a 1D shape.
Create a 2D or 3D array of zeros
For more than one dimension, pass the shape as a tuple. This also answers a common quiz question: the shape of np.zeros((3, 2)) is (3, 2).
import numpy as np
m = np.zeros((3, 2))
print(m)
print("shape:", m.shape)
print()
cube = np.zeros((2, 3, 4))
print("cube shape:", cube.shape, "| size:", cube.size)
print("np.zeros((3, 0)).shape:", np.zeros((3, 0)).shape)
print()
try:
np.zeros(3, 2)
except TypeError as err:
print("np.zeros(3, 2) -> TypeError:", err)
Output:
[[0. 0.]
[0. 0.]
[0. 0.]]
shape: (3, 2)
cube shape: (2, 3, 4) | size: 24
np.zeros((3, 0)).shape: (3, 0)
np.zeros(3, 2) -> TypeError: Cannot interpret '2' as a data type

The first number is rows and the second is columns, so (3, 2) printed three rows of two. A 3D shape like (2, 3, 4) is two blocks of 3 by 4.
np.zeros(3, 2) fails because the second positional argument is dtype, and NumPy can’t use 2 as a type. Always wrap a multi-dimensional shape in its own parentheses.
For working with rows and columns once you have them, see 2D arrays in Python and the NumPy shape guide.
Set the dtype of a NumPy zeros array
dtype changes what the zeros are, and how much memory they take:
import numpy as np
kinds = {"default": None, "int": int, "np.float32": np.float32,
"np.uint8": np.uint8, "bool": bool, "complex": complex}
for label, dt in kinds.items():
z = np.zeros(3, dtype=dt)
print(f"{label:<11} {str(z):<24} {str(z.dtype):<11} {z.nbytes:>2} bytes")
Output:
default [0. 0. 0.] float64 24 bytes
int [0 0 0] int64 24 bytes
np.float32 [0. 0. 0.] float32 12 bytes
np.uint8 [0 0 0] uint8 3 bytes
bool [False False False] bool 3 bytes
complex [0.+0.j 0.+0.j 0.+0.j] complex128 48 bytes

dtype=int gave int64 on this Windows machine. NumPy 1.x used 32-bit integers on Windows, and NumPy 2 changed that, so older tutorials may show int32.
Zeros of type bool are False. float32 and uint8 save memory for large arrays, such as images or machine learning inputs.
Small types have a catch. uint8 holds 0 to 255, and arithmetic wraps around silently:
import numpy as np
pixels = np.zeros(3, dtype=np.uint8)
pixels[0] = 250
pixels += 10
print("250 + 10 in uint8:", pixels)
try:
pixels += 300
except OverflowError as err:
print("OverflowError:", err)
Output:
250 + 10 in uint8: [ 4 10 10]
OverflowError: Python integer 300 out of bounds for uint8
250 plus 10 became 4 with no warning. Adding the plain Python number 300 at least raised OverflowError, because it can’t fit at all. NumPy data types lists every type and its range.
np.zeros_like(): zeros with another array’s shape
np.zeros_like(a) makes a zero array with the same shape and dtype as a. The dtype part is easy to miss:
import numpy as np
prices = np.array([19, 24, 31]) # integers
discounts = np.zeros_like(prices)
discounts[0] = 2.5
print("zeros_like(prices):", discounts, discounts.dtype)
discounts = np.zeros_like(prices, dtype=float)
discounts[0] = 2.5
print("with dtype=float: ", discounts, discounts.dtype)
print()
grid = np.array([[1.5, 2.0], [3.1, 4.8]])
print(np.zeros_like(grid))
print("same as np.zeros(grid.shape, dtype=grid.dtype):",
np.array_equal(np.zeros_like(grid), np.zeros(grid.shape, dtype=grid.dtype)))
Output:
zeros_like(prices): [2 0 0] int64
with dtype=float: [2.5 0. 0. ] float64
[[0. 0.]
[0. 0.]]
same as np.zeros(grid.shape, dtype=grid.dtype): True

zeros_like() copies the dtype too, so an integer template truncates 2.5 to 2.prices held integers, so discounts did too, and 2.5 was stored as 2 without any error. Passing dtype=float fixes it.
For a float template, np.zeros_like(grid) is simply a shorter way to write np.zeros(grid.shape, dtype=grid.dtype), and the last line confirms they match.
Check if an array is all zeros
The reverse question comes up too: is everything in this array still zero? You don’t need to compare each value yourself:
import numpy as np
a = np.zeros((2, 3))
b = a.copy()
b[1, 2] = 0.001
print("a is all zeros:", not a.any())
print("b is all zeros:", not b.any())
print("nonzero values in b:", np.count_nonzero(b))
Output:
a is all zeros: True
b is all zeros: False
nonzero values in b: 1
a.any() is True if any element is nonzero, so not a.any() means all zeros. np.count_nonzero() tells you how many values changed, which caught the single 0.001 in b.
What is the difference between np.zeros and np.empty?
NumPy has a family of these functions. They all take the same shape and dtype arguments and differ only in the starting values:
import numpy as np
print("np.zeros: ", np.zeros(4))
print("np.ones: ", np.ones(4))
print("np.full: ", np.full(4, 7.5))
print("np.empty: ", np.empty(4).shape, "(values are whatever was in memory)")
Output:
np.zeros: [0. 0. 0. 0.]
np.ones: [1. 1. 1. 1.]
np.full: [7.5 7.5 7.5 7.5]
np.empty: (4,) (values are whatever was in memory)
np.ones() fills with 1, and np.full() fills with any value you give it. np.empty() doesn’t fill at all, so its values are whatever was already in that memory.
People reach for np.empty() to save time. Here’s what the timings looked like for ten million values:
import timeit
import numpy as np
n = 10_000_000
def ms(fn, number):
return min(timeit.repeat(fn, number=number, repeat=3)) / number * 1e3
print(f"np.zeros(n): {ms(lambda: np.zeros(n), 20):8.3f} ms")
print(f"np.empty(n): {ms(lambda: np.empty(n), 20):8.3f} ms")
print(f"np.zeros(n) + 1: {ms(lambda: np.zeros(n) + 1, 5):8.3f} ms")
print(f"[0] * n: {ms(lambda: [0] * n, 5):8.3f} ms")
Output:
np.zeros(n): 0.034 ms
np.empty(n): 0.030 ms
np.zeros(n) + 1: 36.610 ms
[0] * n: 26.127 ms
np.zeros() was as fast as np.empty(). NumPy asks the operating system for memory that’s already zeroed, and the real cost shows up on the first write, which is why adding 1 took far longer.
Building a plain list with [0] * n was in the same range as that first write. So use np.zeros() unless you’ll overwrite every value anyway. Creating an empty array in Python explains when np.empty() is the right choice.
np.zeros vs a Python list of zeros
Without NumPy, a list of zeros is [0] * n. It’s fine for one dimension, but the 2D version has a trap:
import numpy as np
row = [0] * 4
print(row, type(row).__name__)
grid = [[0] * 3] * 2
grid[0][0] = 9
print("shared rows: ", grid)
grid = [[0] * 3 for _ in range(2)]
grid[0][0] = 9
print("separate rows:", grid)
print()
arr = np.zeros((2, 3), dtype=int)
arr[0, 0] = 9
print(arr)
print("back to a list:", arr.tolist())
Output:
[0, 0, 0, 0] list
shared rows: [[9, 0, 0], [9, 0, 0]]
separate rows: [[9, 0, 0], [0, 0, 0]]
[[9 0 0]
[0 0 0]]
back to a list: [[9, 0, 0], [0, 0, 0]]
[[0] * 3] * 2 repeats the same inner list twice, so changing one row changed both. A list comprehension builds separate rows, and np.zeros() never has this problem.
NumPy is worth it when you’ll do math on the numbers. For plain lists, creating an array of zeros in Python covers the list, array module and pandas options.
Common np.zeros errors and fixes
These are the errors that come up with np.zeros(), each caught so you can see the message:
import numpy as np
attempts = {
"np.zeroes(3)": lambda: np.zeroes(3),
"np.zeros(3, 2)": lambda: np.zeros(3, 2),
"np.zeros(-1)": lambda: np.zeros(-1),
"np.zeros(3.0)": lambda: np.zeros(3.0),
"np.zeros('3')": lambda: np.zeros("3"),
}
for code, make in attempts.items():
try:
make()
except (AttributeError, TypeError, ValueError) as err:
print(f"{code:<15} {type(err).__name__}: {err}")
Output:
np.zeroes(3) AttributeError: module 'numpy' has no attribute 'zeroes'
np.zeros(3, 2) TypeError: Cannot interpret '2' as a data type
np.zeros(-1) ValueError: negative dimensions are not allowed
np.zeros(3.0) TypeError: expected a sequence of integers or a single integer, got '3.0'
np.zeros('3') TypeError: 'str' object cannot be interpreted as an integer
