Array Attributes
Every ndarray carries a small set of attributes that describe its shape, element type, and memory. Reading them is the first debugging reflex in NumPy: the overwhelming share of array bugs is a shape or dtype mismatch, and printing .shape and .dtype at the top of a function is cheaper than any other diagnostic. This chapter catalogs the attributes and the few methods that accompany them.
Shape, Dimensions, Size
import numpy as np
a = np.zeros((2, 3, 4))
print(a.ndim) # 3 - number of axes
print(a.shape) # (2, 3, 4) - length along each axis
print(a.size) # 24 - total number of elements (product of shape)
v = np.arange(10)
print(v.ndim, v.shape, v.size) # 1 (10,) 10
shape is the contract: a tuple with one entry per axis. The debugging habit is to assert or print it at function boundaries - assert x.shape == (n, d) - so a bad shape fails loudly at the edge instead of three function calls later inside a cryptic broadcasting error. size is the element count, and ndim the axis count; the three are redundant given shape, but each has a use in generic code.
dtype and Memory
a = np.zeros((100, 100), dtype="float64")
print(a.dtype) # float64
print(a.itemsize) # 8 bytes per element
print(a.nbytes) # 80000 - total memory: size * itemsize
i = np.zeros((100, 100), dtype="int32")
print(i.itemsize, i.nbytes) # 4 40000
dtype is the element type object; itemsize its byte width; nbytes the total allocation. For memory planning - "will this array fit?" - nbytes is the number: a 4096x4096 float64 image is 256 MB before anything else is allocated. The dtype object itself is rich (np.dtype("float64").kind returns 'f', .itemsize, etc.), but in practice you compare or print it, and you change it with astype (next topic).
Converting: astype and tolist
a = np.array([1, 2, 3])
f = a.astype("float64") # int64 -> float64, a new array
h = a.astype("float32") # deliberate precision/memory trade
print(f.dtype, h.dtype)
py = f.tolist() # back to plain Python: [1.0, 2.0, 3.0]
print(type(py), type(py[0])) # list, float
back = np.array(py) # and back again - the JSON round trip
astype(dtype) converts element values to a new dtype and returns a new array (the conversion may lose information - float to int truncates). tolist() produces native Python objects, the bridge to JSON serialization, plain-Python consumers, and frameworks that expect lists. The round trip np.array(x.tolist()) is a cheap way to make a copy with a fresh allocation.
Transpose and Flags
m = np.arange(6).reshape(2, 3)
print(m)
print(m.T) # transposed view, shape (3, 2)
print(m.T.shape)
print(m.flags) # memory layout flags
# C_CONTIGUOUS: True F_CONTIGUOUS: False OWNER: True ...
T (or ndarray.transpose()) returns the array with axes reversed - a view, not a copy, which is why transposing a huge array is free. flags reports the memory layout: C_CONTIGUOUS means rows are stored contiguously (the normal case), F_CONTIGUOUS means Fortran-ordered (columns contiguous), and OWNERS says the array owns its buffer. The flags matter when performance is odd (a non-contiguous slice feeding a C routine) and are the ground truth for the memory chapter.
Other Useful Members
a.flat- a flat iterator over the elements (for v in a.flat).a.ravel()/a.flatten()- 1-D form;ravelreturns a view when possible,flattenalways a copy.a.base- the array that owns the memory (None if this array owns it) - the quick test for "am I a view?".a.strides- bytes to step per index along each axis; the low-level explanation of views (memory chapter).np.asanyarray(x)/np.asarray(x)- accept array-likes (lists, tuples, scalars) and return an array, converting only when the input is not already one.
Summary
Know the five attributes cold: shape, ndim, size, dtype, nbytes - and print or assert them at the boundaries of any non-trivial function, because shape and dtype mismatches are the dominant NumPy bug class. Use astype to convert (a new array, possible precision loss), tolist to cross into plain Python, T for a free transposed view, and flags/base/strides when memory behavior itself is the question.