4

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?

3
  • you are overwriting task btw Commented Jun 2, 2020 at 5:08
  • 2
    Other than removing the error (for command in task.commands: print command.actual_cmd), there's nothing wrong with what you have, unless perhaps you have many different object structures with similar loops over them, in which case you could write a function that's more generic - but since you're just printing, that seems unlikely. Commented Jun 2, 2020 at 5:08
  • Looks Pythonic! Commented Jun 2, 2020 at 5:15

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I disagree, I think that OP's code is much more readable at a quick glance than using nested generators.
@OldBunny2800 Maybe so with three nested loops. Not with ten.
3

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.

Comments

Your Answer

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.