Getter and setter for a k-d nested list-of-lists I'm trying to build accessors for a kd nested list-of-lists such as this 4D example (of shape \$ 2 \times 2 \times 2 \times 2 \$):

kd_lol = [
  [
    [
      [0,1],
      [2,3]
    ],
    [
      [4,5],
      [6,7]
    ]
  ], 
  [
    [
      [8,9],
      [10,11]
    ],
    [
      [12,13],
      [14,15]
    ]
  ]
]


I use a list of values to access and modify elements in this nested list-of-lists. 

In the above example, you can access something using [i,j,k,l] where \$ 0 \le i,j,k,l &lt; 2 \$.

I'd like to build a fast, efficient getter in idiomatic Python of the form:

def get(grid, list_of_indices)
def put(grid, list_of_indices, value)


My current implementation is as follows:

def get(grid, list_of_indices):
  cur_grid = grid
  for index in list_of_indices:
          cur_grid = cur_grid[index]
  assert(type(cur_grid) != type([])) # not a list
  return cur_grid


and

def put(grid, list_of_indices, value):
  cur_grid = grid
  for index in list_of_indices[:-1]:
    cur_grid = cur_grid[index]
  assert(type(cur_grid) == type([]))    # must be a 1D list
  assert(type(cur_grid[0]) != type([])) # must be a 1D list
  cur_grid[list_of_indices[-1]] = value


Any critique/suggestions to tidy up or reduce the amount of code above is welcome.

Here's a repl that you can play with.
Some very nice code, overall. There's some minor points I think could be improved, but your general implementation is good.

Nitpicks

I'd like to point out some small 'issues' regarding best-practice and code style:


PEP8 recommends using 4 spaces for indentation. They're not too fond of tabs, but most editors will replace tabs with  spaces automatically.
Using assert is great for catching bugs, but it'd better to raise an exception instead when releasing your code. This allows us to specify a specific exception type (like ValueError or TypeError) and provides some additional stack trace information.
It's important to use good names for objects. You're almost there, but a name like cur_grid may as well just be called current_grid, for clarity.
Instead of manually comparing type, we can use isinstance which allows something like this: assert not isinstance(curr_grid, list).


Implementation

The code as is works just fine if you need to modify a matrix, but wouldn't it be useful to turn it into a separate object altogether? We can do this by creating a custom class:

import collections


class Matrix(collections.UserList):
    """A Matrix represents a collection of nested list objects.
    Methods:
    get -- Get a value from the matrix.
    put -- Put a value into the matrix.
    """
    def __init__(self, data):
        """Constructor.

        :param list data: a matrix (multi-dimensional list).
        :return: None
        """
        super(Matrix, self).__init__(data)

    def get(self, indices):
        """Get a value from the matrix.

        :param iterable indices: iterable containing indices.
                                 the last index must point to an object
                                 that is not a list.
        :return: matrix at indices[-1]
        :raises TypeError: if the last index points to a list
        """
        grid = self.data
        # As assigned by UserList
        for index in indices:
            grid = grid[index]
        if isinstance(grid, list):
            raise TypeError("last element is not 1-dimensional")
        return grid

    def put(self, indices, value):
        """Put value at matrix[indices].

        :param iterable indices: iterable containing indices.
                                 the last index must point to a list.
        :return: None
        :raises TypeError: if the last element is not a list
        :raises TypeError: if the last element contains a list
        """
        grid = self.data
        for index in indices[:-1]:
            grid = grid[index]
        if not isinstance(grid, list):
            error = "expected 'list' node, but got '{}'".format(type(grid))
            raise TypeError(error)
        if isinstance(grid[0], list):
            raise TypeError("last element is not 1-dimensional")
        grid[indices[-1]] = value


Note: the class as implemented must be initialized with a matrix and breaks if an empty iterable is passed for data.