<div dir="ltr">tl;dr What is support like for adding an 'as' clause to comprehension syntax? In order to allow map-then-filter, it might look like something this:<br><br> [y for x in numbers if abs(x) as y > 5]<br><br>I wish to propose an extension to Python comprehension syntax in an attempt to make it applicable in more areas. I'll first describe the deficiency I perceive in the current comprehension syntax and then propose my extension. I'll then note some drawbacks. For the purposes of illustration I'll use list comprehension syntax but I believe everything I say is equally applicable to set and dictionary comprehensions. I'll also talk about lists but again (more or less) everything applies to the more general concept of iterables.<br><br>Comprehensions are essentially a filter followed by a map operation. So if you need to perform a filter operation followed by a map operation over a list, this is pretty convenient in Python. Here we are going to take the absolute value of all even numbers within the range -10 to 10.<br><br> numbers = range(-10, 10)<br> [abs(x) for x in numbers if x % 2 == 0]<br><br>However, if you wish to perform a map operation and *then* a filter operation this is not so convenient, so suppose we wish to obtain the absolute value of all numbers that have an absolute value larger than 5, we can do this by calling the mapped method twice:<br><br> abs(x) for x in numbers if abs(x) > 5]<br><br>This is a bit unsatisfying and even impossible in the case that the mapped method has some side-effect (although arguably if you find yourself in that situation you have taken a mis-step somewhere). An alternative is to apply the mapping first:<br><br> [y for y in (abs(x) for x in numbers) if y > 5]
<br><br>I have to say I quite like this, as it is pretty explicit, but it is a bit unsatisfying that you require only one comprehension for a filter-then-map but two for a map-then-filter.
What would be nice is if we could give a name to the mapped expression and then use that in the filter.<br><br> [abs(x) as y for x in numbers if y > 5]<br><br>I don't like this as it means the order of execution is dependent on whether there is an 'as' clause, in particular the 'if' clause itself may do some computation such as in `if f(y) > 5`.<br><br>An altenative is to allow 'as' expressions in the condition, something like:
<br><br> [y for x in numbers if abs(x) as y > 5]<br><br>Note that it would be possible to map to an intermediate result, such as:<br><br> [y**2 for x in numbers if abs(x) as y > 5]<br><br>Alternatively we could put the 'as' in the pattern:<br><br> [y**2 for abs(x) as y in numbers if y > 5]<br><br>I did not like this as it is obscures the fact that 'x' is being set to each element of 'numbers'. Additionally, we might later wish to adopt a functional programming idiom in which we use 'as' for deconstructive assignment whilst giving a name to the entire matched value, for example:<br><br> [p for (x,y) as p if x > y]<br><br>Or more generally:<br><br> (x,y) as a = f(z)<br><br>But that is getting somewhat off-topic. I promised some drawbacks:<br> * I am confident there are some implementation gotchas nestling in here somewhere.<br> * I could imagine how abuse of such a mechanism to lead to pretty unreadable code.<br> * I'm still not *that* upset by the explicit map first: `[y for y in (abs(x) for x in numbers) if y > 5]`<br> * I could see how it is not immediately obvious what the code does.<br> * It would need to be decided whether you allowed multiple 'as' expression in the condition, particularly using 'and' or 'or' as in 'if f(a) as x > 5 and f(b) as y > 5'<br><br>To summarise:<br> * It's a touch annoying that comprehensions allow filter-then-map but not map-then-filter<br> * Three proposed syntaxes are:<br> * [abs(x) as y for x in numbers if y > 5]<br> * [y for x in numbers if abs(x) as y > 5]<br> * [y**2 for abs(x) as y in numbers if y > 5]<br> * My favourite is the middle one.<br><br>Finally these seem to currently be syntax errors so we should not break any existing code.<br></div>