An ndarray is one block of numbers with a shape and a type. Every value has the same dtype. That is why array * 2 doubles every entry, while a Python list * 2 repeats the list.
Build a small array, read .shape / .dtype / .ndim, and see how list arithmetic differs.
From a list
prices = np.array([10.5, 22.0, 31.0])
print(prices)
print("shape:", prices.shape)
print("ndim:", prices.ndim)
print("dtype:", prices.dtype)A 1-D shape prints as (3,) — the trailing comma is Python’s way to write a one-element tuple.
Two dimensions
Rows are cities (Nairobi, Mombasa, Kisumu). Columns are products A, B, C.
units = np.array(
[
[12, 7, 2],
[9, 6, 0],
[3, 11, 1],
]
)
print(units)
print("shape:", units.shape)
print("ndim:", units.ndim)shape is (rows, cols) — here (3, 3).
Lists do not do elementwise math
py = [12, 7, 2]
arr = np.array(py)
print("list * 2:", py * 2)
print("array * 2:", arr * 2)Use arrays when you want every cell multiplied. Use lists when you want a Python sequence.
One memory buffer
units = np.array(
[
[12, 7, 2],
[9, 6, 0],
[3, 11, 1],
],
dtype=float,
)
print(units.flags["C_CONTIGUOUS"])
print("itemsize:", units.itemsize, "bytes")
print("nbytes:", units.nbytes)The numbers live in one contiguous block. Later chapters show when a slice still points at that block (a view) and when NumPy copies.
dtype for integer literals is usually int64 (or int32 on some builds). Passing dtype=float stores float64.