python - Single Line Nested For Loops

Viewed 11825

What will be python single line equivalent of below code?

l=[]
for i in range(3,5) :
    if i==3:
        for j in range(0,2):
            if i%2:
                l.append(i*j)
            else:
                l.append(i+j)
    else:
        l.append(i)
print(l)

I tried using a single line for nested loop but with only one condition like this:

print([i*j if i%2 else i+j for i in range(3,5) for j in range(0,2)])

Note: Here i want to learn how to use single line equivalent for nested loops with if else condition in both the loops. Also if it's not possible then specify why not!

Thanks in advance :)

5 Answers

You can transform your inner portion into a sequence of lists:

[[i * j if i % 2 else i + j for j in range(2)] if i == 3 else [i] for i in range(3,5)]

Unrolling a 2D iterable is easy in the general case:

[e for row in iterable for e in row]

Combining the two:

[e for row in [[i * j if i % 2 else i + j for j in range(2)] if i == 3 else [i] for i in range(3,5)] for e in row]

You can avoid storing intermediate lists by using generators:

[e for row in ((i * j if i % 2 else i + j for j in range(2)) if i == 3 else [i] for i in range(3,5)) for e in row]

What I came up with was to generate a sequence for each i, then have an enclosing comprehension flatten those into a single list:

[x for y in ((i*j if i%2 else i+j for j in range(0, 2)) if i == 3 else (i,) for i in range(3, 5)) for x in y]
# [0, 3, 4]

Obviously, this is way more complicated and difficult to read than the nested version, and probably performs more poorly.

Here's a hideous monstrosity that does it:

[x for i in range(3,5) for x in ([i*j if i%2 else i+j for j in range(2)] if i==3 else (i,))]

I'm trying hard to make this readable:

In [12]: result = [
    ...:     x
    ...:     for i in range(3, 5)
    ...:     for x in ((i*j if i%2 else i+j for j in range(2))
    ...:               if i ==3 else (i,))
    ...: ]

In [13]: print(result)
[0, 3, 4]

As pointed out in the comments, your looping shouldn't get this complex. You should use functions here to make your code more readable. It will also make the use of list-comprehensions less unwieldy. Your goal should not be "keep things on one line." Your goal should be "keep my code readable, maintainable, and simple, and honest". This list comprehension is none of those things.

Note, I don't expect this to perform better. It requires creating extra anonymous containers or generators\list-comprehensions (which requires creating and calling a function object underneath the hood).

Your goal in writing code shouldn't be "how do I learn to write increasingly complex list comprehensions", rather, it should be "how do I write and organize my code so I can make use of straightforward and readable list comprehensions".

I'd start off by trying something like the following:

[(i*j if i % 2 and i==3
  else i+j if i==3
  else i)
 for j in range(0, 2)
 for i in range(3, 5)]

Nested list comprehension syntax can get quite messy. This is the output:

[0, 4, 3, 4]

I guess you could make this into a set to remove dupes:

result = list(set(result))
# where result is the list above 

[0, 3, 4]

This is what i came up with: print(list(set([i*j if i % 2 else i+j if i==3 else i for i in range(3, 5) for j in range(0, 2)])))

Related