Python offers several ways to work with array-like data structures, each designed for different use cases. Unlike languages such as C or Java where arrays have a single fixed type, Python provides lists, the array module, and NumPy arrays, each with distinct trade-offs in flexibility, memory usage, and performance.
In this guide, you will learn how to declare and use each type of array in Python, understand when to choose one over another, and avoid common pitfalls that can lead to bugs or performance problems.
Python Lists
Lists are the default and most versatile choice for ordered collections in Python. They are dynamic, support mixed types, and require no imports:
arr = []
arr = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True]
numbers = list(range(10))
print(numbers)Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Common List Operations
arr = [1, 2, 3]
arr.append(4) # Add to end: [1, 2, 3, 4]
arr.insert(0, 0) # Insert at index: [0, 1, 2, 3, 4]
arr.extend([5, 6]) # Add multiple: [0, 1, 2, 3, 4, 5, 6]
arr.pop() # Remove last: [0, 1, 2, 3, 4, 5]
arr[0] = 10 # Modify by index: [10, 1, 2, 3, 4, 5]Creating Lists with List Comprehensions
List comprehensions offer a concise way to create lists with transformations or filtering:
squares = [x ** 2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
evens = [x for x in range(20) if x % 2 == 0]
print(evens) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]Creating 2D Lists (Nested Lists)
matrix = [[i * 3 + j for j in range(3)] for i in range(3)]
for row in matrix:
print(row)Output:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]A Common Mistake: 2D Lists with the Multiplication Operator
A frequent source of bugs is creating 2D lists using the * operator:
bad_matrix = [[0] * 3] * 3
bad_matrix[0][0] = 99
print(bad_matrix)Output:
[[99, 0, 0], [99, 0, 0], [99, 0, 0]]Setting bad_matrix[0][0] changed all three rows because they are references to the same list object. Use a comprehension instead:
good_matrix = [[0] * 3 for _ in range(3)]
good_matrix[0][0] = 99
print(good_matrix)The array Module
For memory-efficient storage of homogeneous numeric data, Python's built-in array module stores raw values rather than full Python objects:
import array
int_array = array.array("i", [1, 2, 3, 4, 5])
float_array = array.array("f", [1.0, 2.5, 3.7])
print(int_array) # array('i', [1, 2, 3, 4, 5])
print(float_array) # array('f', [1.0, 2.5, 3.700000047683716])Operations like append and extend work similarly to lists, but type enforcement is strict:
import array
int_array = array.array("i", [1, 2, 3])
int_array.append(4)
print(int_array)
try:
int_array.append("hello")
except TypeError as e:
print(f"Error: {e}")Output:
array('i', [1, 2, 3, 4])
Error: an integer is required (got type str)Common Type Codes
| Code | C Type | Python Type | Size (bytes) |
|---|---|---|---|
'b' |
signed char | int | 1 |
'i' |
signed int | int | 2-4 |
'l' |
signed long | int | 4-8 |
'f' |
float | float | 4 |
'd' |
double | float | 8 |
NumPy Arrays
NumPy is the standard library for scientific computing and data analysis in Python. It provides powerful multidimensional arrays with vectorized operations that are dramatically faster than equivalent list-based code:
pip install numpyimport numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr)
floats = np.array([1, 2, 3], dtype=np.float64)
print(floats)Output:
[1 2 3 4 5]
[1. 2. 3.]Array Initialization Functions
NumPy provides several convenience functions for creating arrays:
import numpy as np
zeros = np.zeros(5)
print(f"zeros: {zeros}")
ones = np.ones(5)
print(f"ones: {ones}")
full = np.full(5, 7)
print(f"full: {full}")
range_arr = np.arange(0, 10, 2)
print(f"arange: {range_arr}")
linspace = np.linspace(0, 1, 5)
print(f"linspace: {linspace}")Output:
zeros: [0. 0. 0. 0. 0.]
ones: [1. 1. 1. 1. 1.]
full: [7 7 7 7 7]
arange: [0 2 4 6 8]
linspace: [0. 0.25 0.5 0.75 1. ]Vectorized Operations
NumPy's greatest strength is performing element-wise operations without explicit loops:
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr * 2)
print(arr ** 2)
print(np.sqrt(arr))
print(f"Sum: {np.sum(arr)}, Mean: {np.mean(arr)}")Output:
[ 2 4 6 8 10]
[ 1 4 9 16 25]
[1. 1.41421356 1.73205081 2. 2.23606798]
Sum: 15, Mean: 3.0Multidimensional Arrays
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(f"Shape: {matrix.shape}")
print(f"Element at row 1, col 2: {matrix[1, 2]}")
zeros_2d = np.zeros((3, 4))
identity = np.eye(3)
print(f"\n3x4 zeros:\n{zeros_2d}")
print(f"\n3x3 identity:\n{identity}")Output:
Shape: (3, 3)
Element at row 1, col 2: 6
3x4 zeros:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
3x3 identity:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]Pre-Allocating Fixed-Size Arrays
When you know the size of your array in advance, pre-allocating avoids repeated resizing:
fixed_list = [0] * 100
print(f"Fixed list length: {len(fixed_list)}")
import numpy as np
fixed_np = np.zeros(1000)
print(f"NumPy array length: {len(fixed_np)}")Output:
Fixed list length: 100
NumPy array length: 1000Memory Comparison
To see the memory difference between the three array types:
import sys
import array
import numpy as np
n = 1000
py_list = list(range(n))
arr_module = array.array("i", range(n))
np_array = np.arange(n, dtype=np.int32)
print(f"List: {sys.getsizeof(py_list):>6} bytes")
print(f"array.array: {sys.getsizeof(arr_module):>6} bytes")
print(f"NumPy: {np_array.nbytes:>6} bytes")Example output:
List: 8056 bytes
array.array: 4200 bytes
NumPy: 4000 bytesThe Python list uses roughly twice the memory because each element is stored as a full Python object with its own overhead, while array.array and NumPy store raw typed values contiguously.
Comparison Summary
| Type | Import Required | Data Types | Speed | Memory | Best For |
|---|---|---|---|---|---|
list |
None | Mixed | Moderate | High | General-purpose programming |
array.array |
array (stdlib) |
Numeric only | Medium | Low | Memory-constrained, no external deps |
numpy.ndarray |
numpy |
Numeric only | Fast | Low | Math, science, data analysis |
Conclusion
For most Python code, lists are the appropriate and simplest choice. They are built into the language, require no imports, and handle a wide variety of use cases.
- When working with large numeric datasets, switch to NumPy arrays for vectorized operations, lower memory usage, and dramatically faster computation.
- The
arraymodule fills a narrow niche where you need compact numeric storage without installing third-party packages. - Choose the array type that matches your data, your performance requirements, and the libraries your project already depends on.