I would write [x for x in y if x >2 and x % 3 == 1]
Someone else consistently doubles up the if and writes [x for x in y if x >2 if x % 3 == 1]
Although I breeze through reading nested for loops in comprehensions, the doubled if format doesn’t scan as well for me, which is probably because I don’t read it often enough.
How about you, what scans best? What do you encounter most often?
I recommend using and rather than multiple if-clauses.
The origin of list comprehensions was from the set builder notation in mathematics where use of ∧ for a logical-and is the norm.
A virtue of list comprehensions is that they can be read left to right in a way that feels like natural English (I’m not sure about other spoken languages). For example, [name.capitalize() for name in users if name.islower() and name not in the blocked_users] reads as “make a list where the name is capitalized for every name in the users list but only if the name is all lowercase and the name is not in the set of blocked users”. We read as plain English, “and” fits better help than multiple ifs.
The norm in the Python world is to use and in list comprehensions rather than multiple-ifs. Many users don’t even know that the latter is possible. For code to be maximally intelligible to others, it should follow their idioms and norms.
The origin of and is from propositional calculus, so it is well suited to logical manipulations. For example, applying De Morgan’s theorem to the OPs example gives, [x for x in y if not (x <= 2 or x % 3 != 1)]. Sometimes this is a simplification or improvement to readability, and sometimes not. But it is harder to do with nested ifs than with and.
Lastly, I agree with @Paddy3118 that the multiple ifs don’t “scan well”.
Thanks for those links.
If it’s a matter of style, I see the use of and, and am more comfortable with writing and reading that. How about you, what’s your preference?
Hmm, not much difference in timings from this little test using Spyder with ipython’s %timeit:
Python 3.10.5 | packaged by conda-forge | (main, Jun 14 2022, 06:57:19) [MSC v.1929 64 bit (AMD64)]
Type "copyright", "credits" or "license" for more information.
IPython 7.33.0 -- An enhanced Interactive Python.
In [1]: %timeit [x for x in range(2**16) if x % 3 == 1 and x % 5 == 1]
4.99 ms ± 26.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [2]: %timeit [x for x in range(2**16) if x % 3 == 1 if x % 5 == 1]
4.97 ms ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [2]:
In [3]: %timeit [x for x in range(2**20) if x % 3 == 1 and x % 5 == 1]
81.2 ms ± 310 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [4]: %timeit [x for x in range(2**20) if x % 3 == 1 if x % 5 == 1]
81.5 ms ± 206 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [5]: