11 ms·
One of the single language features that makes me stay with python is list comprehensions. So maybe something like that: stuff = [cleanup(s) for s in stuff
by atoav 4y ago
One of the single language features that makes me stay with python is list comprehensions. So maybe something like that:
stuff = [cleanup(s) for s in stuff if s.unprocessed]
This, the way you can handle/manipulate strings and the powerful included libraries make python a good language.
Downsides?
Dependency managment, tooling, speed
- snowpid 4y agoList Comprehension is just syntax for functional coding (filter, map) but a very good one!
- von_lohengramm 4y agoI may be the outlier here, but I have a very hard time comprehending list comprehensions. They've always seemed like a SQL query compressed into a single line (or two if you're lucky!): readable when it's simple, Lovecraftian when it's not. I've always found the functional pipeline style much more readable and much easier to explain to others, especially with IDE type hints.
- int_19h 4y agoSQL queries are sequence comprehensions! Python comprehensions in particular are unfortunate because the flow of the query, like in SQL, is not linear - the projection part is written first, but happens last. Thus, reading a non-trivial query requires moving back and forth to resolve the references. Compare that to C# LINQ, which is also a kind of sequence comprehension. That one not only has linear syntax where the data flow in the query is strictly left to right, but it's explicitly defined by mapping it to functional pipeline style. Thus, it ends up being a strict improvement on the latter. (Another example of "good" sequence comprehensions in this sense is XQuery FLWOR)
- maxbond 4y agoIMHO the key to legible comprehensions is to use them only to express composition and to put the logic into verbosely named functions. Chained pipelines (`foo.filter(...).map(...)`) are fine to me, certainly more verbose then comprehensions for better & worse, but I get lost in pipelines expressed as nested functions (`map(filter(a, ...), ...)`).
- dragonwriter 4y ago> they’ve always seemed like a SQL query compressed into a single line (or two if you’re lucky!): If its more than a very simple expression, with one simple for, and no if or very simple if, I try to use at least (more, with parens, if necessary): one line for the result expression, one line for each for clause, and one line for each if clause. But, while the boundary is subjective, it doesn’t take much before functional pipeline style is clearer (Python lambdas being more limited and syntacticaly cumbersome, functional pipeline style isn’t as clean in Python as it is in other languages, though.)
- snowpid 4y agoit depends on your problem but in many cases simple is enough imho. There are map and filter in python avalaible. I just didnt need to use them.
- ogogmad 4y agoBeing pedantic: This isn't true in Haskell, because list comprehensions can use pattern matching, while map+filter can't. heads :: [[a]] -> [a] heads xs = [y | (y:ys) <- xs] -- heads [[1],[],[2,3]] == [1,2] Doing the above using maps and filters looks very different: heads' = map head . filter (not . null) Python doesn't really support Haskell-like pattern matching, so the above doesn't apply.
- pdonis 4y ago> Python doesn't really support Haskell-like pattern matching It does as of 3.10: https://peps.python.org/pep-0622/ https://peps.python.org/pep-0622/ Not as sophisticated as what Haskell can do, I'm sure, but I think it's a good addition to Python.
- _v7gu 4y agoThe thing you're looking for is easily found with a Hoogle search for type (a -> Maybe b) -> [a] -> [b] Which yields mapMaybe: https://hackage.haskell.org/package/base-4.17.0.0/docs/Data-Maybe.html#v:mapMaybe https://hackage.haskell.org/package/base-4.17.0.0/docs/Data-.... so you end up with heads = mapMaybe safeHead
- ogogmad 4y agoThat doesn't look like a direct substitute that covers every case.
- pharmakom 4y agoSorry, but Python list comprehensions are not very good compared to other languages.
- snowpid 4y agowhy?
- pharmakom 4y agoIn F# list comprehensions, for example, you can have full blown expressions such as loops and conditionals. This is really nice for templating. [ if List.isEmpty items then p “No items” else ul [ for item in items do li [ item ] ] ]
- snowpid 4y agoso? if len(items) == 0: return [p("no items")] else: ul( [ li(item) for item in items ] ) I am also not a fan of convoluted list comprehension (in python) so powerful is not necessarly a good thing.
- pharmakom 4y agoThat only works in a single purpose function. And consider this example: [ h1 “items” if List.isEmpty items then p “No items” else ul [ for item in items do li [ item ] ] ]
- snowpid 4y agoh1(items) + rest of my code... I might see what are you using for but I am not sure if concenating implicitly (or with line breaks / spaces) is a good design decision for a language.
- sodapopcan 4y agoList comprehensions are great but certainly not unique to Python.
- curiousgal 4y agoC#'s LINQ is far more powerful in my opinion.
- matsemann 4y agoI have a completely opposite reaction, one of the things I hate the most with Python is its list-incomprehensions. They quickly become hard to read and very imperative, instead of declarative. Something like stuff.filter(it.unprocessed).map(cleanup(it)) conveys the intention behind each statement a hundred times better. And list comprehensions becomes impossible to reason about once they involve flatmapping or other relative simple functional expressions.
- ilyash 4y agoYep. What bothers me is when you start looking at list comprehension is that you don't know if it's filter(), map(), or a combination. You have to read the list comprehension till the end to understand.
- sodapopcan 4y agoI agree very much so. List comprehensions and can be nice and concise and readable in many situations, but due to the whole "one true way" thing in Python, they're used in every situation, even when a map/reduce/filter etc would be better. Although since there are no pipes, I sorta get it.
- MrVandemar 4y agoDefinite agreement. I started a long time ago with Python2, and what attracted me was how simple and readable the syntax was. With Python3 there is a move towards "idiomatic" Python code, which to me means moving away from clear and concise to the old programmer trap/readability nightmare of "look how many things I can get it to do with one line of code!"
- fbdab103 4y agoLet's not even talk about those insane people who use nested list comprehensions. Not sure if it would make my top-5 Python-hate list (1-4 would probably be dedicated to packaging and distribution), but list comprehensions definitely feel like a misstep that contributes little value to the language.
- cortesoft 4y agoI feel like this is a lot cleaner and more clear in Ruby: stuff = stuff.map {|s| cleanup(s) if s.unprocessed }.compact
- nequo 4y agoOnly knowing Python and not Ruby, your snippet looks confusing to me. 1. What does the anonymous function return if s.unprocessed is false? 2. If it returns nothing, do I get a missing value element in the iterable that results from map? 3. And what does it mean to compact the result of the map call? The Python snippet raises none of these questions in me. But it could be because I am familiar with Python’s quirks.
- cortesoft 4y agoThe compact gets rid of all the null values returned when unprocessed is false. Interestingly I wasn't exactly sure what the python code would do, which is why I used the compact. In the python code, does 'stuff' end up only having the result of the cleanup function for items that are unprocessed? Or will it have all of the items, and the ones where unprocessed returns false just end up being returned unmodified? My Ruby version returns the processed subset of items that were unprocessed. The python version was not clear to me, since I don't know what happens if s.unprocessed returns false. So basically, it sounds like we were both confused by the same parts of the language we are unfamiliar with... what happens to elements where s.unprocessed is false? It was clear to me that in Ruby it returns a nil, but I have no idea in Python... what happens?
- nequo 4y agoHuh, our mutual confusion is interesting indeed! Thank you for teaching me something about Ruby. What compact does makes sense. Python’s list comprehension combines map and filter. [process(s) for s in stuff if s.unprocessed] is equivalent to map process (filter isUnprocessed stuff) in Haskell.
- baq 4y agoNothing, literally. This is map+filter optimized (or a for+if+append but using dedicated opcodes for efficiency) so you don’t have to do multiple passes and/or create temporary lists with superfluous elements.
- spicyusername 4y agoI almost always prefer to read nested loops and conditionals than list comprehensions. I find the syntax of list comprehensions ugly and inscrutable.
- xvedejas 4y agoAdditionally I find that it's easier to add complexity to the loops; once you want to start doing nontrivial things in a list comprehension, you often need to rewrite it into a loop first anyway.
- Spivak 4y ago> It's easier to add complexity to the loops This is basically selling point for comprehensions. You can't add complexity to them so any comprehension you see fits a few common loop patterns you can grok immediately. If you see an actual for loop in python it means "pay attention something odd is probably going on."
- gorgoiler 4y agoAnother effective technique for making readable, maintainable code is to break the list comprehension out into a generator (as a closure): This example…: a = … vs = [ v(x) for b in a.f() for x in b if x and c1(x) and not c2(x) ] …becomes: a = … def each_v(): for b in a.f(): for x in b: if not c1(x): # comment continue if c2(x): # comment continue if x: yield v(x) vs = set(each_v()) Line orienting each conditional makes commenting much easier. The collection used for vs is clear. The formatting is less susceptible to being churned up by black. You get to give the thing a name, which helps.
- Kwpolska 4y agoYou can easily replace this with various forms of map/filter in many other popular languages. Assuming cleanUp is a single-argument function/static method: // JS stuff = stuff.filter(s => s.unprocessed).map(cleanUp); // Java stuff = stuff.stream().filter(s -> s.unprocessed).map(Main::cleanUp).toList(); // C# stuff = stuff.Where(s => s.Unprocessed).Select(CleanUp).ToList(); Java streams and C# LINQ extensions also expose other nice data processing functions, eg. sum, takeWhile, distinct. And if you like Python’s dictionary comprehensions: {str(n): n * 2 for n in range(1, 11)} you can use ToDictionary in C#: Enumerable.Range(start: 1, count: 10).ToDictionary(n => n.ToString(), n => n * 2);
- skitter 4y agoThese are also easier to read and write than the Python version, as you can just follow the dataflow from top to bottom (or left to right if it fits into a single line). In Python, your eyes have to jump around to find where to the next bit of processing happens, not just in comprehensions but also with map/filter/list/len and co which are freestanding functions for some reason.
- gsinclair 4y ago“You can…” Sure, but the Python code is so much nicer. I don’t even like Python all that much but it’s comprehensions and generators are very sweet.
- Kwpolska 4y agoI find the non-Python versions easier to read, since they show the exact data flow in order (instead of Python’s [3 for 1 if 2] order) and they allow more advanced scenarios.
- atoav 4y agoSure you can do that and I use things like this all the time in other languages, but the python example was very basic. There are other cases that are sometimes harder to reproduce e.g. when iterating over dictionaries to create a list, when you have tuples etc.
- gdprrrr 4y agoHave you tried Haskell vor Scala?
- mmcnl 4y agoI love list comprehensions but my brain starts melding with nested list comprehensions. The syntax is not obvious and I have to Google it and figure it out again 100% of the time. The JavaScript .map() method is much easier to understand.
- type-r 4y agoI like list comprehensions now after having used the language for a while, but I will say they were the hardest for me to grok when I was first learning the language. even now it can be difficult to follow what's going on if you have nested ones.
- msz-g-w 4y ago> Downsides? Dependency managment, tooling, speed What's wrong with its tooling?