2
\$\begingroup\$

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 < 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.

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Oct 17, 2017 at 19:13
\$\endgroup\$
2
  • \$\begingroup\$ Would you kindly change 'list-of-lists' to 'matrix', since that's the formal definition? \$\endgroup\$ Commented Oct 17, 2017 at 20:41
  • 1
    \$\begingroup\$ @Coal_, from Wikipedia a matrix typically refers to a 2D structure only. Math SE doesn't seem certain either. I'll leave this edit to the wisdom of CRSE. \$\endgroup\$ Commented Oct 18, 2017 at 3:40

1 Answer 1

3
\$\begingroup\$

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.

answered Oct 17, 2017 at 20:20
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.