The best source of information is the official Python tutorial on list comprehensions. List comprehensions are nearly the same as for loops (certainly any list comprehension can be written as a for-loop) but they are often faster than using a for loop.
Look at this longer list comprehension from the tutorial (the if part filters the comprehension, only parts that pass the if statement are passed into the final part of the list comprehension (here (x,y)):
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
It's exactly the same as this nested for loop (and, as the tutorial says, note how the order of for and if are the same).
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
The major difference between a list comprehension and a for loop is that the final part of the for loop (where you do something) comes at the beginning rather than at the end.
On to your questions:
What type must object be in order to use this for loop structure?
An iterable. Any object that can generate a (finite) set of elements. These include any container, lists, sets, generators, etc.
What is the order in which i and j are assigned to elements in object?
They are assigned in exactly the same order as they are generated from each list, as if they were in a nested for loop (for your first comprehension you'd get 1 element for i, then every value from j, 2nd element into i, then every value from j, etc.)
Can it be simulated by a different for loop structure?
Yes, already shown above.
Can this for loop be nested with a similar or different structure for loop? And how would it look?
Sure, but it's not a great idea. Here, for example, gives you a list of lists of characters:
[[ch for ch in word] for word in ("apple", "banana", "pear", "the", "hello")]
Answer from Jeff Tratner on Stack OverflowThe best source of information is the official Python tutorial on list comprehensions. List comprehensions are nearly the same as for loops (certainly any list comprehension can be written as a for-loop) but they are often faster than using a for loop.
Look at this longer list comprehension from the tutorial (the if part filters the comprehension, only parts that pass the if statement are passed into the final part of the list comprehension (here (x,y)):
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
It's exactly the same as this nested for loop (and, as the tutorial says, note how the order of for and if are the same).
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
The major difference between a list comprehension and a for loop is that the final part of the for loop (where you do something) comes at the beginning rather than at the end.
On to your questions:
What type must object be in order to use this for loop structure?
An iterable. Any object that can generate a (finite) set of elements. These include any container, lists, sets, generators, etc.
What is the order in which i and j are assigned to elements in object?
They are assigned in exactly the same order as they are generated from each list, as if they were in a nested for loop (for your first comprehension you'd get 1 element for i, then every value from j, 2nd element into i, then every value from j, etc.)
Can it be simulated by a different for loop structure?
Yes, already shown above.
Can this for loop be nested with a similar or different structure for loop? And how would it look?
Sure, but it's not a great idea. Here, for example, gives you a list of lists of characters:
[[ch for ch in word] for word in ("apple", "banana", "pear", "the", "hello")]
You might be interested in itertools.product, which returns an iterable yielding tuples of values from all the iterables you pass it. That is, itertools.product(A, B) yields all values of the form (a, b), where the a values come from A and the b values come from B. For example:
import itertools
A = [50, 60, 70]
B = [0.1, 0.2, 0.3, 0.4]
print [a + b for a, b in itertools.product(A, B)]
This prints:
[50.1, 50.2, 50.3, 50.4, 60.1, 60.2, 60.3, 60.4, 70.1, 70.2, 70.3, 70.4]
Notice how the final argument passed to itertools.product is the "inner" one. Generally, itertools.product(a0, a1, ... an) is equal to [(i0, i1, ... in) for in in an for in-1 in an-1 ... for i0 in a0]
Videos
Suppose I want to do some list comprehension:
new_list = [x*b for x in a]
Notice how you can easily tell what the list comprehension is doing by just reading left to right - i.e., "You calculate the product of x and b for every x in a."
Now, consider nested list comprehension. This code here which flattens a list of lists into just a single list:
[item for sublist in list_of_lists for item in sublist]
Now, read this line of code:
[i*y for f in h for i in f]
Is this not clearly more annoying to read than the first line I posted? In order to tell what is going on you need to start at the left, then discern what i is all the way at the right, then discern what f is by going back to the middle.
If you wanted to describe this list comprehension, you would say: "Multiply i by y for every i in every f in h." Hence, this feels much more intuitive to me:
[item for item in sublist for sublist in list_of_lists] [i*y for i in f for f in h]
In how I have it, you can clearly read it left to right. I get that they were trying to mirror the structure of nested for loops outside of list comprehension:
for sublist in list_of_lists:
for item in sublist:
item... however, the way that list comprehension is currently ordered still irks me.
Has anyone else been bothered by this?
Possibly counter intuitively, in a nested list comprehension you need to follow the same order of the for loops as with the longhand version. So:
[data[((len(data) - 1) - (8 * i)) - (7 - n)] for i in range(int(len(data) / 8)) for n in range(8)]
So the main difference in your solution, is that the order of the generator part is switched.
To transform:
collection_c = []
for a in collection_a:
for b in collection_b:
collection_c.append(a,b)
You would want to do:
collection_c = [ (a,b) for a in collection_a for b in collection_b]
So in your example you would end up with
new_data = [ data[((len(data) - 1) - (8 * i)) - (7 - n)] for i in range(int(len(data) / 8)) for n in range(8)]
You could use a generator expression to nest the loops and add a filter that makes the IndexError handler obsolete:
candidates = ((x, y) for x in lister[0].conditions for y in x.codes if ',' in y.id)
for x, y in candidates:
if y.id.split(',')[1] == condition:
matcher = x.codenames
Readability would be improved more by using more meaningful names other than x and y here though:
candidates = ((cond, code) for cond in lister[0].conditions for code in cond.codes
if ',' in code.id)
for cond, code in candidates:
if code.id.split(',')[1] == condition:
matcher = cond.codenames
You can use one-line if and continue statements instead of the try statement:
some_var = y.id.split(',')
if len(some_var) < 2: continue
if some_var[1] == condition:
matcher = x.codenames
and replace some_var with a meaningful name.
Look carefully at the parentheses in your one-liner:
print ''.join((letter[i - 1]for i in (int(n) for n in key.split())))
^---------------------------^
The nested generator is simply a sequence that provides values for the outer generator. It could be simplified to
print ''.join((letter[int(i) - 1]for i in (n for n in key.split())))
or just
print ''.join(letter[int(i) - 1] for i in key.split())
An equivalent loop would be
for i in key.split():
print letter[int(i) - 1], # Suppress the newline
Example of nested loops simplification.
from enum import IntEnum
class MyEnum(IntEnum):
FOO_BAR = 0
JOHN_DOE = 1
result = []
for x in MyEnum:
x_splits = []
for s in x.name.split('_'):
x_splits.append( s.capitalize() )
result.append ( ' '.join(x_splits) )
# >>> result
# ['Foo Bar', 'John Doe']
First step:
result = []
for x in MyEnum:
x_splits = [ s.capitalize() for s in x.name.split('_') ]
result.append ( ' '.join(x_splits) )
Second step:
result = []
for x in MyEnum:
result.append ( ' '.join( s.capitalize() for s in x.name.split('_') ) )
Third step:
result = [ ' '.join( s.capitalize() for s in x.name.split('_') ) for x in MyEnum ]
result the same:
# ['Foo Bar', 'John Doe']