Problem: Views vs Copies (When Modifying One Array Changes Another)

December 10, 2025

Subscribe to the Effie Labs Newsletter

You slice an array, modify the slice, and suddenly your original array changed too. This is the view vs copy problem—one of NumPy's most confusing gotchas, and it's bitten me more times than I'd like to admit.

The problem

Here's the classic mistake. You think you're working with a copy, but you're actually modifying the original:

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
slice_view = arr[1:4] # This is a VIEW, not a copy
slice_view[0] = 999 # Modifying the view...
print(arr) # ❌ Original changed!
print(slice_view)
Output:
[1 999 3 4 5]
[999 3 4]

Wait, what? You only modified slice_view, but arr changed too. This happens because basic slicing creates a view—a new array object that shares the same memory as the original.

When you get views vs copies

Not all slicing creates views. Here's when you get what:

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

# Basic slicing → VIEW

view = arr[1:4] # Shares memory with arr
view[0] = 999 # Modifies arr too!

# Fancy indexing → COPY

fancy = arr[[1, 3]] # Creates a new array
fancy[0] = 999 # Doesn't modify arr ✅

# Boolean indexing → COPY

mask = arr > 2
bool_slice = arr[mask] # Creates a new array
bool_slice[0] = 999 # Doesn't modify arr ✅

How to check if it's a view or copy

If you're not sure, you can check if two arrays share memory:

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

view = arr[1:4]
copy = arr[1:4].copy()

# Check if they share memory

print(np.shares_memory(arr, view)) # ✅ They share memory
print(np.shares_memory(arr, copy)) # ✅ They don't share memory

# Or check the base attribute

print(view.base is arr) # True (view's base is arr)
print(copy.base is arr) # False (copy has no base)
Output:
True
False
True
False

The fix: explicit copying

When you need a true copy, use .copy() explicitly:

arr = np.array([1, 2, 3, 4, 5])
slice_copy = arr[1:4].copy()  # Now it's a copy
slice_copy[0] = 999
print(arr)                     # ✅ Original unchanged
print(slice_copy)
Output:
[1 2 3 4 5]
[999 3 4]

Why views exist (and when they're useful)

Views exist for performance. Creating a view is O(1)—it just creates a new array object pointing to the same memory. Creating a copy is O(n)—it has to copy all the data. For large arrays, this matters.

Views are great when you want to modify parts of an array in place. But if you're not sure whether you need a copy, use .copy(). The performance cost is usually negligible compared to the debugging time you'll save.

The key insight

Basic slicing (arr[1:4]) creates views. Fancy indexing (arr[[1, 3]]) and boolean indexing create copies. If you're not sure, use .copy() explicitly. Or check with np.shares_memory() to see if they share memory.

The rule of thumb: if you're going to modify a slice and don't want the original to change, use .copy(). Always.