Emilio M Bumachar Emilio M Bumachar - 10 days ago 7
Python Question

Access nested list element, depth known in runtime

I want to write to an element in a nested list named

foo
, but the nesting depth and indexes is only known at runtime, in a (non-nested!) list variable named
indexes
.

Examples:

If
indexes
is
[4]
, I want
foo[4]
.

If
indexes
is
[4,7]
, I want
foo[4][7]
.

If
indexes
is
[4,7,3]
, I want
foo[4][7][3]
.

What I could think of is to put together the command string (
"foo[4][7][3]"
in the last example), then call
eval
.
That would be
eval("foo["+']['.join([str(n) for n in indexes])+']')
.

That works, and is short enough, but I was hoping for a simpler, more pythonic way.

Does anyone know any alternatives?

Is there a way to not treat the empty list separately? As in:

If
indexes
is
[]
, I want the whole
foo
.

This needs an
if
in the
eval
solution.

EDIT: I need to write to, not read from, the element. I don't think either of the two existing answers, nor the answers to the indicated duplicate, can be used for writing. Apologies for misleading.

Answer

You can use the reduce() function:

from functools import reduce  # Python 3 forward compatibility
import operator

def access(lst, indexes):
    return reduce(operator.getitem, indexes, lst)

You could use list.__getitem__ instead of operator.getitem, but then you limit the application to list objects only (it wouldn't work for tuples or dictionaries, the latter with keys rather than integer indices).

This repeatedly applies the indices, in order, to the previous result (starting the original list). This meets all your criteria, including the empty-list case:

>>> foo = ['a', 'b', 'c', 'd', ['foo', 'bar', 'baz', 'spam', 'eggs', 'ham', 'monty', ['alpha', 'beta', 'delta', 'gamma']]]
>>> access(foo, [])
['a', 'b', 'c', 'd', ['foo', 'bar', 'baz', 'spam', 'eggs', 'ham', 'monty', ['alpha', 'beta', 'delta', 'gamma']]]
>>> access(foo, [4])
['foo', 'bar', 'baz', 'spam', 'eggs', 'ham', 'monty', ['alpha', 'beta', 'delta', 'gamma']]
>>> access(foo, [4, 7])
['alpha', 'beta', 'delta', 'gamma']
>>> access(foo, [4, 7, 3])
'gamma'

If you needed to assign to the last element, keep the last index aside to then assign to that:

if indexes:
    target = access(foo, indexes[:-1])
    target[indexes[-1]] = new_value
else:
    foo = new_value