Quick Answer

A NumPy array holds one type and supports arithmetic on the whole array at once, so arr * 2 doubles every value. A Python list holds anything and list * 2 repeats the list instead. That difference is why numeric Python is built on NumPy.

The difference in one example

This is the whole idea, and it surprises most beginners the first time:

import numpy as np

py_list = [1, 2, 3, 4, 5]
arr = np.array(py_list)

print(py_list * 2)   # [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
print(arr * 2)       # [ 2  4  6  8 10]

Multiplying a list by 2 repeats it. Multiplying an array by 2 doubles every element. Neither is wrong — they are different data types with different meanings — but if you want maths, the list is not doing what you want, and it fails silently by producing a longer list instead of an error.

Doing this with a list requires a loop or a comprehension. With an array it is one expression, and that expression runs in optimised compiled code rather than the Python interpreter. This is called vectorisation, and it is the entire reason NumPy exists.

One array, one type

arr = np.array([1, 2, 3, 4, 5])
print(arr.dtype)     # int64

A list can hold a number, a string and another list at once. An array holds exactly one type, recorded in dtype. That restriction is what buys the speed — every element is the same size, laid out contiguously in memory, so the processor can work through them without checking what each one is.

It also has consequences worth knowing. Putting a float into an integer array converts it. Putting a string in converts the whole array to strings. If an array's behaviour surprises you, printing dtype explains it more often than not.

The operations you will use constantly

arr = np.array([1, 2, 3, 4, 5])

print(arr.mean())    # 3.0
print(arr.max())     # 5
print(arr.sum())     # 15

Then boolean masking, which replaces a filtering loop:

print(arr[arr > 3])  # [4 5]

Read that inside out. arr > 3 produces an array of True/False values, one per element. Using it as an index keeps only the positions that were True. The same pattern powers filtering in pandas, so learning it here pays off twice — see pandas for beginners.

Two dimensions, and the axis argument

m = np.array([[1, 2, 3],
              [4, 5, 6]])

print(m.shape)          # (2, 3)  -- 2 rows, 3 columns
print(m.mean(axis=0))   # [2.5 3.5 4.5]

axis is the argument beginners get wrong most often. axis=0 collapses down the rows, giving one result per column — here, the mean of each column. axis=1 collapses across the columns, giving one result per row.

The reliable way to remember it: the axis you name is the one that disappears. Starting from shape (2, 3), using axis=0 removes the 2 and leaves 3 numbers.

Almost every image, spreadsheet and dataset is a 2-D array underneath, which is why this generalises so far. An image is height by width by colour channels; a table is rows by columns.

How much faster, actually

Worth measuring rather than taking on faith. Doubling one million values, once with a generator over a list and once with NumPy:

import numpy as np, time

n = 1_000_000
big_list = list(range(n))
big_arr = np.arange(n)

t0 = time.perf_counter(); sum(x * 2 for x in big_list)
print(time.perf_counter() - t0)   # about 0.060 s

t0 = time.perf_counter(); big_arr * 2
print(time.perf_counter() - t0)   # about 0.003 s

Roughly twenty times faster on the machine this was run on. The exact ratio varies with hardware and with what you are computing, but the direction never changes, and it widens as the data grows.

The practical rule: if you are writing a loop over numbers in Python, there is usually an array operation that does it in one line and much faster. That single habit is most of what separates slow numeric Python from fast numeric Python.

When to use a list instead

NumPy is not a replacement for lists. Use a plain list when the items are of mixed types, when the collection grows one item at a time, or when you are not doing arithmetic. Appending to an array is expensive because it reallocates, whereas appending to a list is cheap.

A common and perfectly reasonable pattern is to build a list while reading data, then convert it once with np.array(...) before doing the maths.

NumPy is also the foundation under pandas, scikit-learn and most of the scientific Python stack, so the array thinking here transfers directly. If you are heading towards machine learning, our machine learning roadmap covers what comes next.

Frequently Asked Questions

Do I need to install NumPy separately? Yes, with pip install numpy, unless you are using a distribution like Anaconda that bundles it. It is not part of the Python standard library.
What is the difference between an array and a list? An array holds a single type in contiguous memory and supports arithmetic on all elements at once. A list holds anything, grows cheaply, and repeats rather than multiplies when you use the * operator on it.
What does axis=0 mean? It means the operation runs down the rows, producing one result per column. A useful memory aid is that the axis you name is the one that disappears from the shape of the result.
Why is NumPy faster than a Python loop? Because the loop runs in optimised compiled code rather than the Python interpreter, and because same-type contiguous data lets the processor work through it efficiently. The interpreter overhead per element is what you avoid.
Should I learn NumPy before pandas? A little of it, yes. pandas is built on NumPy, and understanding arrays and boolean masks makes pandas far less mysterious. You do not need to master NumPy first — a few hours is enough to make pandas click.