A 2-row × 3-column grid and a 2-element column vector added together by hand. A nested loop walks each row index i, then each cell value x in that row, adding the row's scalar from col[i]. The trace shows new_row filling cell by cell and result growing row by row.

By hand

Outer loop over row index i. Inner loop over each cell value x in grid[i]: add col[i] to x and append to new_row. After the inner loop, append new_row to result.

naive.py
Replay: real traced execution (multi-file project)
grid = [[1, 2, 3], [4, 5, 6]]
col = [10, 20]
result = []
for i in range(len(grid)):
    new_row = []
    for x in grid[i]:
        new_row.append(x + col[i])
    result.append(new_row)
print('RESULT:', result)
  1. grid ← [[1, 2, 3], [4, 5, 6]]

    1grid = [[1, 2, 3], [4, 5, 6]]2col = [10, 20]
    values this step[[1, 2, 3], [4, 5, 6]]grid
  2. col ← [10, 20]

    1grid = [[1, 2, 3], [4, 5, 6]]2col = [10, 20]3result = []
    values this step[10, 20]col
  3. result ← []

    2col = [10, 20]3result = []4for i in range(len(grid)):
    values this step[]result
  4. i ← 0

    3result = []4for i in range(len(grid)):5    new_row = []
    values this step0i
  5. new_row ← []

    4for i in range(len(grid)):5    new_row = []6    for x in grid[i]:
    values this step[]new_row
  6. x ← 1

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
    values this step1x
  7. new_row ← [11]

    6for x in grid[i]:7    new_row.append(x + col[i])8result.append(new_row)
    values this step[] [11]new_row
  8. x ← 2

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
    values this step1 2x
  9. new_row ← [11, 12]

    6for x in grid[i]:7    new_row.append(x + col[i])8result.append(new_row)
    values this step[11] [11, 12]new_row
  10. x ← 3

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
    values this step2 3x
  11. new_row ← [11, 12, 13]

    6for x in grid[i]:7    new_row.append(x + col[i])8result.append(new_row)
    values this step[11, 12] [11, 12, 13]new_row
  12. for x in grid[i]:

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
  13. result ← [[11, 12, 13]]

    7        new_row.append(x + col[i])8    result.append(new_row)9print('RESULT:', result)
    values this step[] [[11, 12, 13]]result
  14. i ← 1

    3result = []4for i in range(len(grid)):5    new_row = []
    values this step0 1i
  15. new_row ← []

    4for i in range(len(grid)):5    new_row = []6    for x in grid[i]:
    values this step[11, 12, 13] []new_row
  16. x ← 4

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
    values this step3 4x
  17. new_row ← [24]

    6for x in grid[i]:7    new_row.append(x + col[i])8result.append(new_row)
    values this step[] [24]new_row
  18. x ← 5

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
    values this step4 5x
  19. new_row ← [24, 25]

    6for x in grid[i]:7    new_row.append(x + col[i])8result.append(new_row)
    values this step[24] [24, 25]new_row
  20. x ← 6

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
    values this step5 6x
  21. new_row ← [24, 25, 26]

    6for x in grid[i]:7    new_row.append(x + col[i])8result.append(new_row)
    values this step[24, 25] [24, 25, 26]new_row
  22. for x in grid[i]:

    5new_row = []6for x in grid[i]:7    new_row.append(x + col[i])
  23. result ← [[11, 12, 13], [24, 25, 26]]

    7        new_row.append(x + col[i])8    result.append(new_row)9print('RESULT:', result)
    values this step[[11, 12, 13]] [[11, 12, 13], [24, 25, 26]]result
  24. for i in range(len(grid)):

    3result = []4for i in range(len(grid)):5    new_row = []
  25. stdout ← RESULT: [[11, 12, 13], [24, 25, 26]]

    8    result.append(new_row)9print('RESULT:', result)
    values this stepRESULT: [[11, 12, 13], [24, 25, 26]]stdout

With NumPy

c = np.array(col).reshape(2, 1) turns the 1-D vector into a (2, 1) column array. a + c broadcasts c across all 3 columns, adding each row's scalar to every element in that row.

library.py
import numpy as np

grid = [[1, 2, 3], [4, 5, 6]]
col = [10, 20]
a = np.array(grid)
c = np.array(col).reshape(2, 1)
result = a + c
print('shape:', a.shape)
print('dtype:', result.dtype)
print('values:', result.tolist())
print('RESULT:', result.tolist())
shape: (2, 3)
dtype: int64
values: [[11, 12, 13], [24, 25, 26]]
RESULT: [[11, 12, 13], [24, 25, 26]]

Implementation notes

  • Broadcasting rule: c is (2, 1), a is (2, 3) — the rows match and the column dimension of c is 1, so NumPy stretches it across all 3 columns without copying memory.
  • Contrast with add-vector-to-rows where the vector was (3,) and broadcast across rows. Here the vector is (2, 1) and broadcast across columns.
  • The .reshape(2, 1) call is the key step — without it, col would be (2,) and NumPy would attempt a row broadcast (wrong shape) and raise an error.
  • Shape, dtype, and values are shown explicitly here because ndarray.__repr__ output varies with NumPy version and print options.