2

I don't have a real reason for doing this, other than to gain understanding, but I'm trying to create a list of lists of lists using list comprehension.

I can create a list of lists just fine:

In[1]: [j for j in [range(3,k) for k in [k for k in range(5,10)]]]
Out[1]: [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]

And I can create a list of lists of lists from either the results of that, for example:

In [2]: [range(0,i) for i in [3,4]]
Out[2]: [[0, 1, 2], [0, 1, 2, 3]]

In [3]: [range(0,i) for i in j]
Out[3]: 
[[0, 1, 2],
 [0, 1, 2, 3],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4, 5],
 [0, 1, 2, 3, 4, 5, 6],
 [0, 1, 2, 3, 4, 5, 6, 7]]

But when I try to combine it into a single statement it goes awry:

In [4]: [range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
---------------------------------------------------------------------------
TypeError: range() integer end argument expected, got list.

Am I missing some brackets somewhere?

3
  • May be a try worth: multi = lambda default, dims: [multi(default, dims[1:]) for i in xrange(dims[0])] if dims else default Commented Feb 2, 2012 at 18:37
  • 1
    Note that [k for k in range(5,10)] means the same thing as range(5,10) by itself. Commented Feb 2, 2012 at 19:22
  • I like the wide range of answers! Very helpful :-) Commented Feb 3, 2012 at 10:07

6 Answers 6

3

Try the following:

[[range(0, j) for j in range(3, i)] for i in range(5, 10)]

This results in the following list of lists of lists:

>>> pprint.pprint([[range(0, j) for j in range(3, i)] for i in range(5, 10)])
[[[0, 1, 2], [0, 1, 2, 3]],
 [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]],
 [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5]],
 [[0, 1, 2],
  [0, 1, 2, 3],
  [0, 1, 2, 3, 4],
  [0, 1, 2, 3, 4, 5],
  [0, 1, 2, 3, 4, 5, 6]],
 [[0, 1, 2],
  [0, 1, 2, 3],
  [0, 1, 2, 3, 4],
  [0, 1, 2, 3, 4, 5],
  [0, 1, 2, 3, 4, 5, 6],
  [0, 1, 2, 3, 4, 5, 6, 7]]]

The best way to understand what is happening in a list comprehension is to try to roll it out into normal for loops, lets try that with yours and then mine to see what the difference is:

x = [range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
# equivalent to
a, b, c, x = [], [], [], []
for k in range(5, 10):
    a.append(k)
for k in a:
    b.append(range(3, k))
for j in b:
    c.append(j)
for i in c:
    x.append(range(0, i))

At the end of this x would be equivalent to your list comprehension, however of course this code will not work because b (and c) will be lists of lists, so i will be a list and range(0, i) will cause an error. Now obviously this is not what you intended to do, since what you would really like to see is those for loops nested instead of one after the other.

Lets look at how mine works:

x = [[range(0, j) for j in range(3, i)] for i in range(5, 10)]
# equivalent to
x = []
for i in range(5, 10):
    a = []
    for j in range(3, i):
        a.append(range(0, j)):
    x.append(a)

Hope this helped to clarify!

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

Comments

1

From your question:

In[1]: [j for j in [range(3,k) for k in [k for k in range(5,10)]]]
Out[1]: [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]

range takes integer parameters

So when you do

[range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]

it is the equivalent of saying

L = []
for j in [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]:
    L.append(range(0,i))

Of course, this will fail because each i is a list and range doesn't take list parameters.

Other answers here show you how to fix your error. This response was to explain what went wrong with your initial approach

Hope this helps

Comments

1

To troubleshoot this, I ran each step of the list comprehension at a time.

>>> [k for k in range(5,10)]
[5, 6, 7, 8, 9]

>>> [range(3,k) for k in [k for k in range(5,10)]]
[[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]

Your problem is here, because it feeds lists to the next range() instead of ints:

>>> [j for j in [range(3,k) for k in [k for k in range(5,10)]]]
[[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]

You can flatten this list using lambda and reduce:

# reduce(lambda x,y: x+y,l)
>>> reduce(lambda x,y: x+y, [range(3,k) for k in [k for k in range(5,10)]])
[3, 4, 3, 4, 5, 3, 4, 5, 6, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 8]

But you will still only nest two lists deep:

>>> [range(0,i) for i in reduce(lambda x,y: x+y, [range(3,k) for k in [k for k in range(5,10)]])]
[[0, 1, 2], [0, 1, 2, 3], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6, 7]]

If you want to go three lists deep, you need to reconsider your program flow. List comprehensions are best suited for working with the outermost objects in an iterator. If you used list comprehensions on the left side of the for statement as well as the right, you could nest more deeply:

>>> [[[range(j, 5) for j in range(5)] for i in range(5)] for k in range(5)]

Comments

1

That's because:

    [range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
                    ^here i == j == range(3,k) - it's a range, not integer

You probably wanted to do:

    [range(0,j) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]] for j in i]
                    ^ i is still a list, iterate it with j                              here

You do of course know that your first statement can be reduced to :

[range(3,k) for k in range(5,10)]

??

Comments

1
[range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]

Because i is a list, you need list comprehend on that too:

[[range(0,h) for h in i] for i in [...]]

In your code:

print [
    [range(0,h) for h in i] for i in [    # get a list for i here. Need to iterate again on i!
        j for j in [                      # [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
            range(3,k) for k in [        
                k for k in range(5,10)    # [5,6,7,8,9]
            ]
        ]
    ]
]

# output:
[[[0, 1, 2], [0, 1, 2, 3]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6, 7]]]

Also, you have one unnecessary comprehension in there. This will produce the same result:

print [
    [range(0,i) for i in j] for j in [ # get a list for j here. Need to iterate again on j!
        range(3,k) for k in [          # [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
            k for k in range(5,10)     # [5,6,7,8,9]
        ]
    ]
]

As an aside, this is about the point where a list comprehension becomes less desirable than a recursive function or simple nested for loops, if only for the sake of code readability.

Comments

0

Apparently, in the first range (range(0,i)), i is [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]

You probably need to flatten that list before you call the range function with it.

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.