The fate of reduce() in Python 3000 (2005)
artima.com
artima.com
By far the most common use of lambda for me is the "key" argument for sort() or sorted(). As in something like:
foo.sort(key=lambda x:x[2])
Yes, there are itemgetter and attrgetter in operator module, but I always have to look up their names and writing the lambda expression is simply faster.
And no, I don't want to 'def' every small function I might need, pep-8 recommendations aside. Key as mentioned is one use, but there are other cases where it's especially useful
Performance reasons aside, I strongly disagree with this statement. Anyone can see that `filter` is going to apply the predicate to the iteratable in the first statement. The second statement is super awkward, declares a temp var `x` which to my limited Pythonic eyes looks like it's just calling the identity function on `x` that comes back from the "filter".
Again, I have no doubt Python is optimized to perform better with the second form, but that seems a limitation in the interpreter. The syntax to non-Python devs is pretty raunchy imho.
[x | x <- s, p x]There is going to be a pretty sharp break between people who accept that working with a collection deserves special syntax support and those who don't. The readability issue for me isn't that the syntax doesn't look nice, it is that I've invested years into making a very efficient little mental space that deals with "data2 = fn(data1)" style constructs and I want to use it here like I do everywhere else.
Some silly-high percentage of for loops can be cast into this data2 = fn(data1). Why am I being told that it isn't readable? If this isn't easy to read, then something is wrong with the function syntax.
A chaining operator, to recast fn3(fn2(fn1(data))) => data > fn1 > fn2 > fn3 with reduce is much more readable, debuggable and easy to reason about than having to remember what syntax todays language uses to talk about collections and how that works in to the flow of data in my program.
The real issue is the lack of a lambda syntax with a pythonic feel.
Which of these is clearer?
[x * y for x in S if P(x) > 0]
map(lambda x: x * y, filter(lambda x: P(x) > 0, S))I highly recommend to become familiar with the functools, itertools and collections modules to anybody using Python and not using them regularly.
Comprehensions are just plain faster, especially if you perform say filter and map at the same time.
That and a lambda implementation that only a mother could love, lack of tail call optimization etc.
What bothers me about comprehensions is they come with a separate set of rules, like LOOP in Common Lisp or templates in C++; but I'm pretty sure the restrictions are part of what makes them fast.
> filter(P, S) is almost always written clearer as [x for x in S if P(x)]
sure, Guido, if you think it's clear that the variable x is in the enclosing scope...
Python 3.7.1 (default, Nov 28 2018, 21:47:25)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: lookup = {}
In [2]: [x for (x, lookup[x]) in ('ab', 'cd', 'ef') if x in lookup]
Out[2]: ['a', 'c', 'e']
In [3]: lookup
Out[3]: {'a': 'b', 'c': 'd', 'e': 'f'} [(x, lookup.setdefault(x, y)) for (x, y) in ('ab', 'cd', 'ef')]
comes close), what's happening makes a lot more sense. It works exactly the same way as any other scope: first checks locally, then nonlocally or globally. You're modifying a nonlocal. The syntax you're using to modify the nonlocal is surprising, but that's the surprising part, not the ability to modify a nonlocal.1. The syntax of comprehensions led me to think that they introduce some kind of a variable binding, such as `let` in many Lisps, and that binding `lookup[x]` would have been a syntax error. Of course Python didn't introduce anything like that for comprehensions, and it's just a normal assignment operation, with an extra scope inserted in Python 3 to stop `x` leaking into the enclosing scope.
2. Because of the swap idiom `x, y = y, x` I thought tuple assignment happens somehow in parallel, so my code should have caused an undefined variable error, even if it is not a syntax error. But of course it just evaluates the right hand side first and then assigns to the left hand side in left-to-right order, which the language reference https://docs.python.org/3/reference/simple_stmts.html#assign... notes "sometimes result[s] in confusion" (near the end of the section).
>>> lookup = {}
>>> for (x, lookup[x]) in ('ab', 'cd', 'ef'):
... pass
...
>>> lookup
{'a': 'b', 'c': 'd', 'e': 'f'}
This makes is much more clear that there is just a double assignment, and that comprehension are pretty close to regular `for` statement.(this was a great example btw -- I write python3 a lot, and I was staring at it for a long time before Icould figure it out)
Python 3.7.1 (default, Nov 6 2018, 18:49:54)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.1.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: lookup = {}
In [2]: a, lookup[a] = 'xy'
In [3]: a
Out[3]: 'x'
In [4]: lookup
Out[4]: {'x': 'y'}
I wasn't previously aware that this was possible, but I guess it's no different from a = 'x'
lookup[a] = 'y' # i.e. lookup['x'] = yComprehensions are often slower if you're applying a function regardless, definitely so if that function is a builtin.
Strangely FP is not popular among python developers.
for x in map(func, iterable):
...
is cleaner than for x in (func(y) for y in iterable):
...
and shorter than for y in iterable:
x = func(y)
...
But that mostly only makes sense if you already have a suitable named function (e.g. str), and I usually end up going with the third variant anyway.There's an analogous case with filter and `if not cond: continue`.
map and filter calls are trivially rewritten to generator expressions, but it's not necessarily an improvement.
Technically nothing, but at the same time if you already have a function (especially a builtin) map/filter is more convenient and can be faster. And they're first-class objects which comprehensions are not, which can be convenient.
The goal is not necessarily to prune all redundancies: reduce already existed when sum/min/max/any/all were added, despite the latter being fairly simple special cases of the former (not quite entirely for min/max, but not far).
Programming language archaeology is a field that ought to get more attention.