Origins of Python's “Functional” Features (2009)
python-history.blogspot.com
python-history.blogspot.com
There are some gotchas where you need to explicitly copy (itertools.tee) iterators when you have multiple consumers, and be careful with mutable values, but it's manageable.
It would be nice to use curried functions as functools.partial gets verbose, but at that point you're far from Pythonic code.
One solution would be, I think, to always pass tee-d copies of iterators and never(!) any iterator like it is (because you don't know whether next() has side effects or not). The other solution would be to not ever do anything lazy. One could also always consume all iterators fully. That way any generator re-use would be apparent immediately.
Some function that moves a generator forward just one or two items (something with the semantics of findFirst, for example), can subtly introduce bugs.
I don't see how find_first would cause any more problems than filter or sum.
See here:
This itertool may require significant auxiliary storage (depending on how much temporary data needs to be stored). In general, if one iterator uses most or all of the data before another iterator starts, it is faster to use list() instead of tee().
https://docs.python.org/2/library/itertools.html#itertools.t...
If you get some huge collection to scan, or wants to sync your code by sharing an iterator internal state, you may want to break that rule. But they are just too troublesome for my taste.
https://github.com/sfermigier/awesome-functional-python
I'm going to include this blog post as well.
Pull requests are welcome, BTW.
An explicit loop is easier to read as a rule than the corresponding reduce() code. See http://stackoverflow.com/questions/15995/useful-code-which-u...
list(filter(lambda x : ('widgets' in x), mixed_widgets))[0]['widgets']
I almost always go for more lines of readable code rather than less lines of unreadable code. But in cases like this, you can either write that as one line of garbage unreadable code or six lines of garbage unreadable code. So I'd rather just leave the overall codebase more dense to make it easier to understand the program flow, and then leave a comment explaining what that line is doing.
[x for x in mixed_widgets if 'widgets' in x][0]['widgets']
More readable? I dunno. More "Pythonic"? Definitely.Related: I wish the list type in Python included an analogue to dict's ".get(key, default)" operation.
I believe that the list comprehension will be faster than filter, but as always, any time you replace readable code with unreadable code for performance reasons, you damn well better time it.
In [11]: timeit.timeit('''list(filter(lambda x : ('widgets' in x), mixed_widgets))[0]['widgets']''', '''nw={'abc': 'def'};w={'widgets':'s'};mixed_widgets=[nw]*100+[w]+[nw]*100''', number=100000)
Out[11]: 2.6956532129988773
In [12]: timeit.timeit('''[x for x in mixed_widgets if 'widgets' in x][0]['widgets']''', '''nw={'abc': 'def'};w={'widgets':'s'};mixed_widgets=[nw]*100+[w]+[nw]*100''', number=100000)
Out[12]: 0.5911771030077944
But not generating the list at all is still going to be faster (with bigger gains for bigger data) In [13]: timeit.timeit('''next(x for x in mixed_widgets if 'widgets' in x)['widgets']''', '''nw={'abc': 'def'};w={'widgets':'s'};mixed_widgets=[nw]*100+[w]+[nw]*100''', number=100000)
Out[13]: 0.3324074839911191 from functools import partial
to_strings = partial(map, str)
# vs
def to_strings(seq):
return (str(elem) for elem in seq) def to_strings(seq):
return map(str, seq)
Generally, when the operation I'm applying to each element happens to already be a named function, I find "map(f, seq)" preferable to "(f(x) for x in seq)".However comprehensions are not that harder when one finally decides to understand how they work. Not as readable as map() IMHO. Example:
Ruby
[1, 2, 3].map {|x| x*x} # object.method(args)
vs Python [x*x for x in [1, 2, 3]]
where we have the function first, then the definition of the variable, then the data. This is the opposite of the object.method OO notation and using a variable before defining it is not what we usually do. But it's almost the usual mathematical notation "for i in set do f(i)" with the function at the beginning.Not a big deal.
About a problem raised in a comment of the post (which is from 2009): this is Guido (2009) about the lack of tail call optimization in Python http://neopythonic.blogspot.it/2009/04/tail-recursion-elimin...
(x for x in mixed_widgets if 'widgets' in x)[0]['widgets']
but this doesn't work, since you can’t do indexing [0] on a generator expression. No matter, next() returns the first value of any iterator: next(x for x in mixed_widgets if 'widgets' in x)['widgets']
Also, I think it the repetition of x in the 'x for x in' part is a bit ugly, we can fix that by moving the ['widgets'] attribute retrieval operation to inside the generator expression: next(x['widgets'] for x in mixed_widgets if 'widgets' in x)mixed_widgets.filter { |x| x.contains('widgets') }[0]['widgets']
def find(predicate, iterable, default=None):
"""Returns the first value that matches predicate, otherwise default=None"""
return next(
(x for x in iterable if predicate(x)),
default
)
Which turns the expression to find(lambda x: 'widgets' in x, mixed_widgets)['widgets']`def (x,y): x+y`
This makes it sound like that was just how it was implemented. Nothing fundamentally wrong with lambda i guess
def (x,y): x += y def f(x, y): x+y
is valid one-line syntax for a regular function definition (that in this case computes `x+y` and returns `None` becaus Python does not have implicit returns). Maybe the parser could distinguish between them by saying that "if the function definition is all on one line and the function is missing its name, it's a lambda": def f(x,y): return x+y
g = def x,y: x+y
assert f(3,4) == g(3,4)
assert f != g
That said I have no
Nothing gives me the willies in mathematical code like writing out Greek letters. I don't know why, it just does. The fact that the otherwise excellent Stan (https://mc-stan.org) only accepts ASCII variable names is depressing. I would love to be able to actually write `map(λ x: x+7, range(5))`. I think a big reason why people don't use `map`/`reduce`/etc. more is that writing anonymous functions is still verbose (`lambda` is just too long a word) and relatively hard to read compared to list comprehension. Better still a syntax like `x -> x+7` would be ideal, since I'm pretty sure `->` would be a unique syntax element.