Reputation: 2613
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.
Upvotes: 2
Views: 298
Reputation: 1122042
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
Upvotes: 4
Reputation: 16184
You can set item
to foo
, then proceeds with the indexes list to access deeper nested elements:
def access (foo, indexes):
item = foo
for index in indexes:
item = item[index]
return item
Upvotes: 2