Copies vs views

NumPy · Practical · 12 min

Accidental sharing causes spooky bugs; accidental copying wastes memory. You need to predict both.

A view is a new array object that looks at the same data buffer as another array, possibly with different shape, strides or offset. A copy has its own buffer. Changing a view changes the original; changing a copy does not.

The rules of thumb:

  • Views: basic slicing (a[1:5], a[:, 0], a[::2]), reshape (when possible), ravel (when possible), .T, transpose, a.view(dtype), np.broadcast_to.
  • Copies: fancy indexing (a[[1, 2]]), boolean masks (a[a > 0]), arithmetic results, astype, flatten, np.array(a), a.copy().
  • Neither: plain assignment b = a creates no new array at all, only a second name for the same object.

"When possible" matters. reshape returns a view if the existing memory layout allows it; reshaping a transposed or step-sliced array may silently copy. If you need a guarantee, check rather than assume.

How to check:

  • b.base is a is True when b is a view whose memory belongs to a; a copy has base is None.
  • np.shares_memory(a, b) gives an exact answer (it can be slow); np.may_share_memory is a quick conservative test.

A classic bug: you slice a small window out of a huge array and keep it around. The view keeps the entire original buffer alive, so memory never gets freed. Call .copy() on small pieces you keep long-term.

Functions that take an input and modify it in place (like a.sort() or np.copyto) change every view of that data too. When writing functions, avoid surprising callers: either document in-place behaviour or work on a copy.

Example

import numpy as np

a = np.arange(6)
s = a[1:4]                  # view
f = a[[1, 2, 3]]            # copy (fancy index)
r = a.reshape(2, 3)         # view

print(s.base is a, f.base is None)      # True True
print(np.shares_memory(a, r))           # True

s[0] = 100
f[1] = -1
print(a)                    # [  0 100   2   3   4   5]  only s affected

t = r.T                     # view, not contiguous
flat = t.reshape(-1)        # layout forces a copy here
print(np.shares_memory(flat, a))        # False

big = np.zeros(10_000_000)
keep = big[:10].copy()      # lets 'big' be freed later
del big

Remember

  • Basic slices, reshape, transpose and .view give views
  • Fancy indexing, boolean masks, arithmetic and astype give copies
  • b = a copies nothing; it's the same object
  • Check with .base or np.shares_memory when unsure

Watch out for

  • Keeping a tiny slice of a huge array alive, which pins the whole buffer in memory.

Check yourself

  1. Which of these creates an array that shares memory with `a`?
  2. `b = a.reshape(3, 4)` on a contiguous `a` of size 12, then `b[0, 0] = 7`. Is `a[0]` now 7?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: numpy.org/doc/stable/user/basics.copies.html