Reputation: 5157
What is an elegant way to iterate the nested object in Python? I am currently using nested loop as below.
for job in jobs:
for task in job.tasks:
for command in task.commands:
print command.actual_cmd
Is there a better way which is more Pythonic?
Upvotes: 4
Views: 1061
Reputation: 7241
It is pythonic. Already.
But if you worry about this goes to 10+ levels deep, with only the innermost loop has anything interesting, one thing you can consider is to create a generator. Your code can become:
def get_command(jobs):
for job in jobs:
for task in job.tasks:
for command in task.commands:
yield command
for command in get_command(jobs):
print command.actual_cmd
So the whole purpose is to avoid excessive indentation.
To make it generic for multiple level, so you don't worry even if it is 100+ levels deep:
def get_nested(objlist, attribs):
for x in objlist:
if len(attribs) == 0:
yield x
else:
x_dot_attr = getattr(x, attribs[0])
for y in get_nested(x_dot_attr, attribs[1:]):
yield y
for command in get_nested(jobs, ["tasks", "commands"]):
print command.actual_cmd
But no, this generalization doesn't make it more pythonic. It only avoids excessive indentation.
Upvotes: 3
Reputation: 78780
You can set up chained generators to keep the indentation level down.
iter_tasks = (task for job in jobs for task in job.tasks)
iter_commands = (command for task in iter_tasks for command in task.commands)
for command in iter_commands:
print(command.actual_cmd)
I agree with OldBunny2800 that in the case of three nested loops chaining generators may not gain you much in terms of readability.
If your nested logic goes deeper than that, the generators start to become attractive. Not only is the indentation level kept in check, you can also assign a meaningful variable name to each generator, effectively giving for
loops a name.
Upvotes: 3