Senior 5 min · March 16, 2026
NumPy Indexing and Slicing — Beyond the Basics

NumPy Indexing — Unintended View Dropped Accuracy 92%→37%

A NumPy slice created a view that mutated training array, dropping accuracy 92%→37%.

N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Lessons pulled from things that broke in production.

Follow
Production
production tested
June 10, 2026
last updated
1,554
articles · all by Naren
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • NumPy offers four indexing methods: basic slicing (view), integer fancy indexing (copy), boolean indexing (copy), and field access for structured arrays.
  • Basic slices always return a view — they share memory with the original. Modifying the view changes the original.
  • Fancy indexing (integer arrays) always returns a copy, even if indices are in sequence.
  • Boolean indexing also returns a copy — the original array stays untouched.
  • Use np.shares_memory() or the .base attribute to check if an array is a view.
  • Biggest mistake: assuming a slice is independent; call .copy() explicitly when needed.
✦ Definition~90s read
What is NumPy Indexing and Slicing?

NumPy indexing is the mechanism for selecting subsets of data from ndarray objects. Unlike Python lists, where slicing always creates a copy, NumPy's basic slicing (using : and integer indices) returns a view — a new array object that shares the underlying memory buffer with the original.

NumPy indexing lets you pick specific elements from a grid of numbers.

This is a performance optimization: no data is copied, so operations on views are fast and memory-efficient. But it's also a landmine: modifying a view silently mutates the original array, and when you pass a view into a function that expects an independent copy (e.g., a machine learning model's preprocessing pipeline), you can get catastrophic accuracy drops — like the 92%→37% collapse this article dissects.

NumPy provides three indexing modes. Basic slicing (e.g., arr[1:5, 2:4]) returns views. Fancy indexing (integer arrays or lists, e.g., arr[[0, 2, 4]]) and boolean indexing (e.g., arr[arr > 0]) always return copies — they cannot produce views because the selected elements are not contiguous in memory. This distinction is critical: mixing slicing with fancy indexing (e.g., arr[1:3, [0, 2]]) triggers fancy indexing rules, producing a copy even where slicing alone would have given a view.

Additional indexing tools include np.newaxis (or None) to insert a new axis of size 1, and Ellipsis (...) to represent multiple colons in full generality. These are syntactic sugar for reshaping and slicing, but they follow the same view/copy rules.

The power of combining techniques — say, boolean indexing on a slice — can create subtle bugs where a view is unexpectedly modified downstream. Understanding exactly when you get a view vs. a copy is not academic; it's the difference between a model that works and one that silently fails.

In the ecosystem, NumPy indexing is the foundation for Pandas .iloc/.loc, TensorFlow/Eager tensors, and PyTorch tensor indexing — all inherit the same view semantics. Alternatives like numpy.copy() or ndarray.copy() force explicit copies, but most practitioners don't use them until after a bug surfaces.

This article shows you how to predict and prevent the view-vs-copy trap before it destroys your accuracy.

Plain-English First

NumPy indexing lets you pick specific elements from a grid of numbers. Think of it like highlighting cells in a spreadsheet — some selections are just a window into the original data (changes affect the source), others are a new separate copy that you can modify without touching the original.

NumPy indexing is a performance optimization that returns views instead of copies for basic slicing, but this shared memory can silently corrupt your data pipelines. A single in-place modification on a view can mutate the original training array, causing accuracy to collapse from 92% to 37% without any error or warning. Understanding when indexing returns a view versus a copy is essential to prevent this class of bugs in production ML systems.

How NumPy Indexing Creates Views, Not Copies — And Why That Destroys Accuracy

NumPy indexing is the mechanism for selecting subsets of array elements using bracket notation, slices, or boolean masks. The core mechanic: most indexing operations return a view — a reference to the original data buffer — not a copy. This means modifying the result modifies the original array, and memory is shared unless you explicitly call .copy().

In practice, basic slicing (e.g., arr[0:5, 2:4]) always returns a view, while advanced indexing (fancy indexing with lists or boolean arrays) returns a copy. The key property: views are O(1) in memory and time to create, but they break isolation. When you train a model on a sliced subset and later normalize that slice in-place, you corrupt the original dataset. This is how a 92% accuracy drops to 37% — the validation set gets silently mutated.

Use views when you need fast, memory-efficient access to large arrays and you control the full lifecycle. Avoid them when passing slices to black-box functions or when data must remain immutable. In production ML pipelines, always assume indexing returns a view and explicitly copy before any in-place operation.

View vs. Copy Is Not Obvious
arr[0:5] returns a view; arr[[0,1,2,3,4]] returns a copy. The same bracket syntax, completely different memory semantics.
Production Insight
ML pipeline where training data is sliced via arr[:split_idx] and then normalized in-place — the validation slice (arr[split_idx:]) gets silently normalized with training statistics.
Symptom: validation accuracy spikes then crashes after first epoch because validation data distribution shifts mid-training.
Rule: always call .copy() on any slice that will be passed to a function you don't control, or use np.array(slice, copy=True).
Key Takeaway
Basic slicing returns a view — modifying it mutates the original array.
Advanced indexing (lists, boolean masks) returns a copy — but this is not guaranteed by the API.
Explicit .copy() is the only safe default when data integrity matters.
NumPy Indexing: Views vs Copies Trap THECODEFORGE.IO NumPy Indexing: Views vs Copies Trap How slicing creates views; fancy/boolean indexing returns copies Basic Slicing Creates view, shares memory Integer Array Indexing Fancy indexing, returns copy Boolean Indexing Filtering arrays, returns copy Advanced Indexing Integer arrays always copy Combined Techniques Powerful but can cause unexpected views Correct Copy/View Use .copy() to avoid mutation ⚠ Slicing returns view; modifying it alters original array Use .copy() explicitly when you need independent data THECODEFORGE.IO
thecodeforge.io
NumPy Indexing: Views vs Copies Trap
Numpy Indexing Slicing

Basic Slicing — Views, Not Copies

Basic slicing uses integers, slices (start:stop:step), and np.newaxis. It always returns a view — a window into the same memory. This is the most common pattern and the source of most confusion.

ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np

a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

# Slicing returns a view
view = a[1:, 2:]
print(view)
# [[ 6  7]
#  [10 11]]

# Modifying the view modifies the original
view[0, 0] = 99
print(a[1, 2])  # 99 — the original changed

# Use .copy() when you want independence
safe = a[1:, 2:].copy()
safe[0, 0] = 0
print(a[1, 2])  # still 99
Output
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 6 7]
[10 11]]
99
99
Think of a view as a window
  • The window doesn't own the items; it just points to them.
  • Any change you make through the window changes the shelf.
  • Calling .copy() builds a new shelf with identical items.
  • Always ask: "Do I need to modify the original? If not, .copy()."
Production Insight
Production bug: A data pipeline sliced a large array for parallel processing and each subprocess modified its slice, corrupting the shared source array.
Fix: Extract slices with .copy() before distributing to workers.
Rule: If multiple consumers might modify a slice, copy it first.
Key Takeaway
Basic slicing = view.
Modify the slice, modify the original.
Use .copy() to break the link.

Integer Array Indexing — Fancy Indexing

Pass an array of indices to select specific elements. This always returns a copy, and the output shape matches the index array shape. It's called "fancy indexing" and gives you powerful reordering and selection.

ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np

a = np.array([10, 20, 30, 40, 50])

# Select elements at positions 0, 2, 4
print(a[[0, 2, 4]])  # [10 30 50]

# Reorder and repeat elements
print(a[[4, 4, 2, 0]])  # [50 50 30 10]

# 2D fancy indexing
m = np.arange(12).reshape(3, 4)
rows = np.array([0, 1, 2])
cols = np.array([0, 2, 3])
print(m[rows, cols])  # [0, 6, 11] — picks m[0,0], m[1,2], m[2,3]

# Use np.ix_ to select a submatrix (outer indexing)
print(m[np.ix_([0, 2], [1, 3])])
# [[ 1  3]
#  [ 9 11]]
Output
[10 30 50]
[50 50 30 10]
[ 0 6 11]
[[ 1 3]
[ 9 11]]
Production Insight
Fancy indexing copies data even if you select the same elements in order. This breaks lazy chaining: a[[0, 1, 2]] is a copy, not a view.
Memory spike: Fancy indexing a large array creates a new allocation; avoid in tight loops.
Rule: Prefer basic slicing for read-only access; use fancy indexing only when you need non-contiguous selection.
Key Takeaway
Integer array indexing = copy.
Output shape matches the index array, not the original.
Use np.ix_ for submatrix selection with broadcast semantics.

Boolean Indexing — Filtering Arrays

Pass a boolean array of the same shape to select elements where the condition is True. This is how you filter arrays without writing a loop. Always returns a copy.

ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np

temps = np.array([22.1, 18.4, 35.7, 29.3, 15.0, 38.2])

# All temperatures above 30 degrees
hot = temps[temps > 30]
print(hot)  # [35.7 29.3 ... wait, 29.3 < 30]
print(temps[temps > 30])  # [35.7 38.2]

# Compound conditions — must use & and |, not and/or
extreme = temps[(temps < 18) | (temps > 35)]
print(extreme)  # [18.4 ... ]
print(temps[(temps < 18) | (temps > 35)])  # [15.  38.2]

# np.where: replace values that fail the condition
clipped = np.where(temps > 35, 35.0, temps)
print(clipped)  # caps at 35.0
Output
[35.7 38.2]
[15. 38.2]
[22.1 18.4 35. 29.3 15. 35. ]
Production Insight
Boolean indexing is great for filtering but it creates a copy, so memory can double for large arrays.
Missing parentheses cause silent bugs: (temps < 18) | (temps > 35) without parentheses throws an ambiguous error.
Rule: Always wrap each condition in parentheses when using & or |.
Key Takeaway
Boolean indexing = filter + copy.
Parentheses matter: (cond1) & (cond2).
Use np.where for conditional replacement without a loop.

np.newaxis and Ellipsis

np.newaxis inserts a new dimension — it is just an alias for None. The ellipsis ... means 'all the dimensions in between'. These are crucial for broadcasting and nD array manipulation.

ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

v = np.array([1, 2, 3])  # shape (3,)

# Turn a row vector into a column vector
col = v[:, np.newaxis]  # shape (3, 1)
print(col.shape)  # (3, 1)

# Ellipsis: useful for nD arrays
data = np.zeros((2, 3, 4, 5))
print(data[0, ..., 2].shape)   # (3, 4) — first slice, last slice, middle left alone
print(data[..., -1].shape)     # (2, 3, 4) — all dims except the last
Output
(3, 1)
(3, 4)
(2, 3, 4)
Production Insight
newaxis is a no-copy operation — it just changes the shape metadata. Use it to align dimensions for broadcasting without memory overhead.
Ellipsis can be ambiguous if you have many dimensions; use explicit colons for clarity.
Rule: Prefer np.newaxis over reshape when you only need to add a dimension for operations.
Key Takeaway
np.newaxis = None = insert dimension, no copy.
Ellipsis = 'all remaining dimensions'.
Use for broadcasting compatibility and clean nD indexing.

Combining Indexing Techniques — Power and Pitfalls

You can mix basic slicing with fancy indexing or boolean indexing in the same expression. The result follows the copy/view rules per axis: any axis using a slice stays a view, any axis using fancy/boolean becomes a copy. The combined result is always a copy if any axis uses fancy indexing.

ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

arr = np.arange(24).reshape(2, 3, 4)

# Basic slice on first axis, fancy on second: result is a copy
result = arr[0, [0, 2], :]  # shape (2, 4)
print(result.base is arr)  # False

# Basic slice on first, boolean on second: also copy
mask = np.array([True, False, True])
result2 = arr[0, mask, :]
print(result2.base is arr)  # False

# All axes basic: view
view = arr[0, :, 1:3]
print(view.base is arr)  # True
Output
False
False
True
Production Insight
Mixed indexing often surprises engineers: a mix that looks like slicing may still produce a copy if one axis uses fancy indexing.
Don't assume .base is None for copies; it may point to a larger intermediate.
Rule: When debugging, explicitly check np.shares_memory(arr, subset) to be sure.
Key Takeaway
Any fancy or boolean axis forces the entire result to be a copy.
Basic slices stay views only when all axes are slices.
Check memory sharing with np.shares_memory, not intuition.

Advanced Indexing — Why Integer Arrays Return Copies (and Why That Matters)

Basic slicing returns a view. Fancy indexing (integer arrays) returns a copy. This isn't an implementation quirk — it's a contract about memory layout.

When you pass a list of indices like arr[[3, 1, 2]], NumPy cannot guarantee a contiguous memory block in the original array's stride pattern. The result is a new array with its own memory buffer. That means mutations on the result don't affect the original. Good for data integrity. Bad for memory budgets on large arrays.

Production trap: People assume all indexing behaves the same. They modify a fancy-indexed slice expecting to update the source array, and wonder why their pipeline silently corrupts downstream logic. This is why we separate read-only feature extraction (fancy indexing) from in-place transformations (basic slicing).

Integer array indexing is also how you implement shuffle, random sampling, and reordering without copying a full array. Use np.random.choice with replace=False to generate indices, then index into your training data. That's a copy, but it's explicit — no hidden views biting you during model training.

AdvancedIndexingCopy.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// io.thecodeforge — python tutorial

import numpy as np

# Sensor readings — original data
sensor_log = np.array([12.4, 13.1, 11.8, 14.0, 12.9])

# Fancy indexing: integer array
selected = sensor_log[[1, 3, 0]]
selected[0] = 99.9  # mutation

print("Original after mutation:", sensor_log)
# Output: [12.4 13.1 11.8 14.0 12.9] — no change

# Basic slicing: view behavior
view = sensor_log[1:4]
view[0] = 77.7

print("Original after view mutation:", sensor_log)
# Output: [12.4 77.7 11.8 14.0 12.9] — changed
Output
Original after mutation: [12.4 13.1 11.8 14.0 12.9]
Original after view mutation: [12.4 77.7 11.8 14.0 12.9]
Production Trap:
Fancy indexing returns a copy. If you're modifying data in-place expecting the original to update, you're creating silent data drift. Use basic slicing for in-place updates, integer arrays for read-only feature extraction.
Key Takeaway
Integer array indexing returns a copy. Basic slicing returns a view. Never assume — verify. Check np.shares_memory(original, result) if you're unsure.

Slicing in NumPy — Stride Patterns That Will Bite Your Performance

NumPy slicing isn't memory-safe like Python lists. It's a view into the same buffer with a new stride configuration. Fast? Yes. Dangerous? Extremely.

When you write arr[::2], NumPy doesn't copy data — it changes the step in strides. That means the slice shares memory with the parent array. Mutate the slice, corrupt the original. This is the #1 source of 'why did my validation set leak into training' incidents.

The step parameter isn't just for reversing arrays. It's how you implement downsampling, decimation, and strided convolutions without allocation. But with great power comes segfault-prone code if you're not tracking memory ownership.

Real production pattern: Decimating a time series? Use slicing with step to reduce sample rate. But if you need to persist the downsampled data, call .copy() explicitly. Your future self debugging a memory corruption at 2 AM will thank you.

Negative steps create reversed views. arr[::-1] is a view, not a copy. Reversing a 100-million element array? That's O(1) with slicing, O(n) with np.flip. Know the difference before you write that batch processing script.

SliceMemoryLeak.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// io.thecodeforge — python tutorial

import numpy as np

# 1 billion data points from sensor array
raw_data = np.arange(1_000_000_000, dtype=np.int64)

# Downsampled view — no memory allocated
decimated = raw_data[::10]

# Mutation: oops — this corrupts the original
decimated[0] = -999

print(raw_data[0])  # Output: -999 — corruption

# Safe pattern: explicit copy
safe_downsample = raw_data[::10].copy()
safe_downsample[0] = -1

print(raw_data[0])  # Output: -999 — original unchanged
print(safe_downsample[0])  # Output: -1
Output
-999
-999
-1
Senior Shortcut:
Use np.shares_memory(a, b) to detect accidental view aliasing before it hits production. Add this check in your CI pipeline for functions that slice large arrays.
Key Takeaway
Slicing with step creates a view, not a copy. Use .copy() when you need an independent buffer. Always check memory sharing when mutating slices of large arrays.

Fancy Indexing with np.ix_ — Cartesian Product Selection Without Loops

Standard fancy indexing with integer arrays selects elements element-wise: you must pass arrays of equal shape, or broadcasting kicks in, which often fails for disjoint row/column selections. That is why np.ix_ exists. It constructs open mesh arrays from your row and column indices, enabling Cartesian product selection without explicit loops or reshaping. The WHY: matrix operations like cross-tabulation or submatrix extraction require every combination of rows and columns — a task that manual broadcasting is error-prone and slower. np.ix_ returns a tuple of arrays that, when used together in indexing, broadcasts exactly as needed, yielding the full Cartesian subset. This avoids Python-level loops and keeps the operation vectorized. Performance gain is substantial for large datasets because NumPy handles the broadcasting natively in C. Always reach for np.ix_ when you need a submatrix from arbitrary rows and columns — it is cleaner and faster than any manual approach.

ix_selection.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
// io.thecodeforge — python tutorial

import numpy as np

arr = np.arange(20).reshape(4, 5)
rows = [0, 2]
cols = [1, 3, 4]

# Cartesian product via np.ix_
subset = arr[np.ix_(rows, cols)]
print(subset)
Output
[[ 1 3 4]
[11 13 14]]
Production Trap:
Without np.ix_, arr[[0,2], [1,3,4]] would throw an IndexError because array shapes (2,) and (3,) do not broadcast. Always check index dimensions.
Key Takeaway
Use np.ix_ for submatrix extraction from arbitrary row/column lists — it guarantees correct Cartesian broadcasting.

Structured Array Field Indexing — Selecting by Column Type, Not Position

When your data is tabular with mixed types (e.g., CSV with int, float, string), a structured NumPy array stores columns as named fields. Indexing with field names bypasses integer positions, making code self-documenting and robust to column reordering. The WHY: positional indexing breaks when column order changes — a common production pain. Field names decouple column selection from layout. Use dtype with names and formats, then index as arr['field_name'] to get a view of that column. You can even pass a list of field names to extract multiple columns as a new structured array. This returns a copy because the resulting dtype differs from the original. For mixed types, field indexing avoids manual type coercion and is faster than pandas for simple column drops or renames. Never hardcode column indices in production pipelines — always use field names.

structured_field.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.thecodeforge — python tutorial

import numpy as np

dt = np.dtype([('name', 'U10'), ('age', 'i4'), ('wage', 'f8')])
data = np.array([('Alice', 30, 75000.0), ('Bob', 25, 62000.0)], dtype=dt)

# Select by field name — view
ages = data['age']
print(ages)

# Select multiple fields — returns copy
subset = data[['name', 'wage']]
print(subset)
Output
[30 25]
[('Alice', 75000.) ('Bob', 62000.)]
Production Trap:
Field indexing with a single name returns a view; with a list of names returns a copy. Modifying the view modifies the original array — a common cause of silent data corruption.
Key Takeaway
Always index structured arrays by field name, not position — protects against schema changes and improves readability.
● Production incidentPOST-MORTEMseverity: high

Corrupted Training Data from an Unintended View

Symptom
Model accuracy dropped from 92% to 37% after a week without any code changes. The training data was being modified during preprocessing.
Assumption
The engineer assumed that slicing always creates a new array, so modifying the slice was safe.
Root cause
A basic slice was used to select a subset of features, but subsequent data cleaning steps modified the slice, altering the original training array in place.
Fix
Replace the slice with .copy() or use fancy indexing for the feature subset (which returns a copy).
Key lesson
  • Never assume a slice is independent — verify with np.shares_memory().
  • When dealing with shared memory arrays, copy before mutation.
  • If a pipeline includes multiple transformation steps, make explicit copies at the boundaries.
Production debug guideSymptom → Action guide for production incidents involving unexpected array mutations.4 entries
Symptom · 01
Two arrays change together when you only modify one
Fix
Check if one is a view of the other. Use np.shares_memory(arr1, arr2) or arr1.base is arr2
Symptom · 02
Memory usage spikes after an indexing operation
Fix
Check if the indexing returned a copy (fancy/boolean) instead of a view (slice). Use np.shares_memory on the result.
Symptom · 03
Unexpected shape mismatch in downstream code
Fix
Verify the indexing method: basic slice reduces dimension only if using integers. Fancy indexing preserves dimensions with [].
Symptom · 04
Changes to a slice don't affect the original as expected
Fix
You likely used fancy or boolean indexing somewhere, causing a copy. Revert to basic slicing if you need a view.
★ Quick Debug: View vs. Copy CheckThree commands to diagnose whether an array is a view or copy.
Need to know if arr is a view or copy
Immediate action
Check .base attribute
Commands
print(arr.base) # None = own data, otherwise = parent array
print(np.shares_memory(arr, original)) # True = view
Fix now
If view and you need independence: arr = arr.copy()
Want to ensure an operation returns a view+
Immediate action
Use only basic slicing (start:stop:step) with no integer lists or boolean masks
Commands
view = original[1:5, 2:7] # always view
print(np.shares_memory(view, original)) # True
Fix now
If you must use integer lists, wrap with np.ix_ and slice the result
Memory doubled after indexing a large array+
Immediate action
Check if indexing used fancy or boolean
Commands
result = arr[[0, 2, 5]] # copy → memory spike
result = arr[0:6:2] # view → no extra memory
Fix now
Replace fancy indexing with slicing where possible; if not, accept the memory cost

Key takeaways

1
Basic slicing returns a view
mutating it mutates the original. Call .copy() when you need independence.
2
Fancy indexing (integer arrays) always returns a copy, even when the indices are in order.
3
Boolean indexing filters by condition and returns a copy
the original is unchanged.
4
np.ix_ constructs an open mesh for selecting submatrices with fancy indexing.
5
np.newaxis is just None
it inserts a length-1 dimension without copying data.

Common mistakes to avoid

5 patterns
×

Assuming a slice is a copy

Symptom
Modifying a slice changes the original array, causing silent data corruption in pipelines.
Fix
Use .copy() explicitly when you need independence. Verify with np.shares_memory().
×

Using and/or instead of &/| for boolean conditions

Symptom
ValueError: The truth value of an array with more than one element is ambiguous.
Fix
Always use & (and) and | (or) with parentheses: (cond1) & (cond2). Never use Python's and/or.
×

Confusing a[0] with a[[0]]

Symptom
IndexError or unexpected shape reduction when selecting a single row.
Fix
a[0] reduces dimensions (basic indexing); a[[0]] keeps dimensions (fancy indexing). Use a[0:1] for a slice that keeps dimensions.
×

Not using np.ix_ for submatrix extraction

Symptom
Fancy indexing with two index arrays picks diagonal elements instead of block.
Fix
Use m[np.ix_(rows, cols)] for outer product selection; otherwise m[rows, cols] selects pairs.
×

Modifying a sliced array in parallel processes

Symptom
Dataset gets corrupted because each process shares the same underlying memory.
Fix
Copy the slice before passing to workers: worker_data = data[chunk_slice].copy()
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
What is the difference between a view and a copy in NumPy? How do you ch...
Q02JUNIOR
Why does modifying a NumPy slice affect the original array?
Q03SENIOR
How does boolean indexing differ from fancy indexing in terms of memory?
Q04SENIOR
What does np.ix_ do and when would you use it?
Q05SENIOR
Explain what happens when you mix basic slicing with fancy indexing in t...
Q01 of 05SENIOR

What is the difference between a view and a copy in NumPy? How do you check which you have?

ANSWER
A view shares memory with the original array; modifications affect both. A copy has its own memory. Use arr.base is not None to check if it's a view, or np.shares_memory(arr, original) for a definitive answer. Basic slicing always returns a view; fancy and boolean indexing return copies.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
How do I know if an operation returns a view or a copy?
02
Why can't I use 'and' and 'or' with NumPy boolean arrays?
03
What is the difference between a[0] and a[[0]]?
04
Can I get a view from fancy indexing?
05
What is the ellipsis (...) in NumPy indexing?
N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Lessons pulled from things that broke in production.

Follow
Verified
production tested
June 10, 2026
last updated
1,554
articles · all by Naren
🔥

That's Python Libraries. Mark it forged?

5 min read · try the examples if you haven't

Previous
NumPy Broadcasting — How It Actually Works
26 / 51 · Python Libraries
Next
NumPy Shape Manipulation — reshape, flatten, ravel, transpose