Reputation: 8607
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?
Upvotes: 2
Views: 13105
Reputation: 6352
[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.
Upvotes: 1
Reputation: 208705
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!
Upvotes: 3
Reputation: 36777
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)]
??
Upvotes: 1
Reputation: 22415
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)]
Upvotes: 1
Reputation: 114035
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
Upvotes: 1
Reputation: 7684
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.
Upvotes: 0