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]
Does anybody else just not like the syntax for nested one-line list comprehension?
python - How to write a nested for loop as a one liner? - Stack Overflow
Nested for loops in python in a single line - Stack Overflow
Nested for loop in Python in single line - iterate with array - Stack Overflow
Is a list comprehension the same as a for loop?
When should I not use a one-line comprehension?
What is the difference between [x for x in data if cond] and [expr for x in data]?
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?
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.
You can use re.sub() to do exactly what you want:
import re
names = ['Mrs Marple', 'Maj Gen Smith', "Tony Dobson's"]
replace = ['Mrs ', 'Maj ', 'Gen ']
names = [re.sub(r'(Mrs\s|Maj\s|Gen\s|\'s$)', '', x) for x in names]
print(names)
Output:
['Marple', 'Smith', 'Tony Dobson']
the problem is due to name = name.replace(e, '') statement in the for loop, and as we can't use assignment operator in comprehensions, you used name.replace(e, '') but again replace() method is not inplace as the string in python is not mutable.
Solution I that I have written is based on using reduce, here were replacing all the occurrences of elements in sequence replace.
from functools import reduce
names = ['Mrs Marple', 'Maj Gen Smith', "Tony Dobson's"]
replace = ['Mrs ','Maj ','Gen ']
result = [reduce(lambda str, e: str.replace(e, ''), replace, name) for name in names]
Here is the result
print(result)
['Marple', 'Smith', "Tony Dobson's"]
The solution by @chrisz works but if replace list is generated on the fly or is too long, we won't be able to form a regex for it. This solution works pretty much in any scenario.