Search code examples
pythonnumpytuplesiterable-unpacking

Why do my numpy constructs ignore tuple deconstruction?


edit: Let me clarify the mystery variables from original post

Assign some variables

WHITE = 0 #UP
BLUE = 1 #DOWN
ORANGE = 2 #LEFT
GREEN = 3 #FRONT
RED = 4 #RIGHT
YELLOW = 5 #BACK

First, I create a janky matrix:

cube = np.array([
        np.array([
          np.array([WHITE, WHITE, WHITE]),np.array([WHITE, WHITE, WHITE]), np.array([WHITE, WHITE, WHITE])
        ]),
        np.array([
         np.array([BLUE, BLUE, BLUE]), np.array([BLUE, BLUE, BLUE]), np.array([BLUE, BLUE, BLUE])
        ]),
        np.array([
         np.array([ORANGE, ORANGE, ORANGE]), np.array([ORANGE, ORANGE, ORANGE]), np.array([ORANGE, ORANGE, ORANGE])
        ]),
        np.array([
         np.array([GREEN, GREEN, GREEN]), np.array([GREEN, GREEN, GREEN]), np.array([GREEN, GREEN, GREEN])
        ]),
        np.array([
         np.array([RED, RED, RED]), np.array([RED, RED, RED]), np.array([RED, RED, RED])
        ]),
        np.array([
         np.array([YELLOW, YELLOW, YELLOW]), np.array([YELLOW, YELLOW, YELLOW]), np.array([YELLOW, YELLOW, YELLOW])
        ])
      ])

The np.arrays are one of my attempts at debugging.

Next, I attempt to swap columns, rows, etc. with tuple deconstruction:

rubiks[LEFT][:,2], rubiks[DOWN][0] = rubiks[DOWN][0], rubiks[LEFT][:,2]

The result is as if it were applied iteratively:

[[1 1 1]
  [1 1 1]
  [1 1 1]]

 [[2 2 1]
  [2 2 1]
  [2 2 1]]

It should be:

[[2 2 2]
  [1 1 1]
  [1 1 1]]

 [[2 2 1]
  [2 2 1]
  [2 2 1]]

I assume I am misunderstanding something, but shouldn't this swap the values like intended?


Solution

  • The slicing on the right side generates references (or 'view' in numpy's terminology). If you force a copy, you can get the expected behavior.

    import numpy as np
    a = np.array([1, 2])
    a[:1], a[1:] = a[1:], a[:1]
    print(a) # [2 2]
    
    import numpy as np
    a = np.array([1, 2])
    a[:1], a[1:] = a[1:].copy(), a[:1].copy()
    print(a) # [2 1]
    

    Multi-variable assignment is essentially a tuple packing & unpacking. Numpy slice at the right side of an assignment expression will only provide a reference instead of a copy at the time when the right-side tuple is made. When the second assignment take effect, the underlying storage of a has already been modified.

    https://numpy.org/doc/stable/reference/arrays.indexing.html#basic-slicing-and-indexing

    In general, avoid figuring out if a numpy slice would automatically be a copy. Take a copy when it is logically necessary.