Here is one pretty effective way to do this that will work with non-quadratic matrices:
DIRS = NONE, UP, DOWN, LEFT, RIGHT = 'unshifted', 'up', 'down', 'left', 'right' def shift(matrix, direction, dist): """ Shift a 2D matrix in-place the given distance of rows or columns in the specified (NONE, UP, DOWN, LEFT, RIGHT) direction and return it. """ if dist and direction in (UP, DOWN, LEFT, RIGHT): n = 0 if direction in (UP, DOWN): n = (dist % len(matrix) if direction == UP else -(dist % len(matrix))) elif direction in (LEFT, RIGHT): n = (dist % len(matrix[0]) if direction == LEFT else -(dist % len(matrix[0]))) matrix[:] = list(zip(*matrix))
The conclusion ( note ) that the results are cumulative, since the operations are performed in place, and the shift is applied to the result of the previous call):
no shift: [1, 2, 3] [4, 5, 6] [7, 8, 9] [10, 11, 12] up: [4, 5, 6] [7, 8, 9] [10, 11, 12] [1, 2, 3] down: [1, 2, 3] [4, 5, 6] [7, 8, 9] [10, 11, 12] left: [2, 3, 1] [5, 6, 4] [8, 9, 7] [11, 12, 10] right: [1, 2, 3] [4, 5, 6] [7, 8, 9] [10, 11, 12] no shift: [1, 2, 3, 4] [5, 6, 7, 8] [9, 10, 11, 12] up: [5, 6, 7, 8] [9, 10, 11, 12] [1, 2, 3, 4] down: [1, 2, 3, 4] [5, 6, 7, 8] [9, 10, 11, 12] left: [2, 3, 4, 1] [6, 7, 8, 5] [10, 11, 12, 9] right: [1, 2, 3, 4] [5, 6, 7, 8] [9, 10, 11, 12]