Live data from Hacker News

Functional programming with Python

ua.pycon.org

41–50 of 51 posts

Re: Functional programming with Python

#41
post #10

Earlier quoted context omitted.

Short answer: not really. Long answer: In [1]: from operator import attrgetter In [2]: class Person: ...: def __init__(self, name): ...: self.name = name ...: In [3]: me = Person('walrus') In [4]: getattr(me, 'name') Out[4]: 'walrus' In [5]: attrgetter('name')(me) Out[5]: 'walrus' This is potentially useful if you're using `map`: In [6]: people = [me, Person('aschwo')] In [7]: list(map(lambda obj: getattr(obj, 'name'…

On the last point, a list comprehension is just a much a functional programming idiom--Python got them from Haskell! I use both in my code, whichever is shortest. PS: map returns a list, no need to call list() on it.

In Python 3, map returns a iterator, not a list:

  In [1]: sq = lambda x: x * x
  
  In [2]: map(sq, [1,2,3,4])
  Out[2]: 
  
  In [3]: list(_)
  Out[3]: [1, 4, 9, 16]

Re: Functional programming with Python

#42
post #32

Earlier quoted context omitted.

> ...faster and often clearer than lambdas. $ python -m timeit 'import string;from operator import itemgetter;[itemgetter('slice') for x in string.letters]' 100000 loops, best of 3: 14.2 usec per loop $ python -m timeit 'import string;from operator import itemgetter;[lambda x: x.slice for x in string.letters]' 100000 loops, best of 3: 10.4 usec per loop > Also: namedtuple is great instead of "using classes as attribu…

You're measuring the wrong thing. itemgetter() is supposed to be called just once and it's the returned function that is used in the loop/map. It should be: python -m timeit 'import string;from operator import itemgetter; getter=itemgetter('slice'); [getter(x) for x in string.letters]'

True. But you should also put the setup in a -s option, because you're not timing the imports. And you don't really need the loop, because timeit does a bunch of loops for you:

    >python -m timeit -s "from operator import itemgetter; elem0 = itemgetter(0)" "elem0('s')"
    10000000 loops, best of 3: 0.095 usec per loop

    >python -m timeit -s "from operator import itemgetter; elem0 = lambda x: x[0]" "elem0('s')"
    10000000 loops, best of 3: 0.138 usec per loop

Re: Functional programming with Python

#43
post #27

It seems to me if you want to write code like this, you shouldn't write it in Python. Python has much simpler, built-in ways to do most of the things he's suggesting. For instance, he says "good" for this code: reduce(operator.add, map(len, ss)) No! The simpler (and more efficient, as it doesn't have to build a list of results!) way to do that is using the built-in sum() with a generator expression: sum(len(s) for s…

This example was given to describe map/reduce pattern, not advice to use it instead of sum:

    reduce(operator.add, map(len, ss))
You know, it's hard to understand new concept. And it's twice harder to explain new concept with sophisticated code examples. So I used as simple code and situations as possible. The same for square_sum - it's easier "to see" functional pattern in map(f,l) than in (f(x) for x in l).

Regarding to debug function and partial function application, - it depends. As for me

    debug = partial(log, "debug")
is easier and meaningfully describe what's actually going on. But here we have only one function, what if you have to create 5-6 same functions? Would you prefer "def" syntax instead of partial? I have one more example of using this pattern in more sophisticated situation - code listing for dictionary binding to list of functions, "partial" looks really naturally.

Re: Functional programming with Python

#44
post #18

Note that `reduce` has been, if not deprecated, then at least discouraged (not that I agree with this, but reduce gets taken away in Python 3, so it's probably good form to stop using it). The author does `reduce(operators.add, lst)` a lot; you can do that using `sum(lst)` instead.

reduce() is just moved to a library instead of being a builtin, it's not really a big deal. And of course sum() is better, addition is just the typical example for exposition (he later notes that shorter forms are always better). Whether it's good form to stop using it only depends on whether you prefer more explicit for-loops.

Yup. Just pointing out that "idiomatic Python" is to prefer sum() to reduce().

Re: Functional programming with Python

#45

Earlier quoted context omitted.

Yea, but still gross. map() isn't even an endomorphism.

I'm not sure what you mean. map is not supposed to be an endomorphism, rather it takes a function f and returns a list homomorphism.

I think it'd be nice if it were an endomorphism, though; Making map(f, tuple) -> tuple, map(f, list) -> list, etc. would save users from having to write tuple(map(f, tuple)) (which is something I've done a bunch).

Re: Functional programming with Python

#46
post #18

Note that `reduce` has been, if not deprecated, then at least discouraged (not that I agree with this, but reduce gets taken away in Python 3, so it's probably good form to stop using it). The author does `reduce(operators.add, lst)` a lot; you can do that using `sum(lst)` instead.

I was writing a really long response to your post, explaining how reduce/inject/fold is abstracting out an absurdly common iteration pattern, and using it can expose structural similarities between many superficially different computations, and how (unlike map and filter) it can't be expressed as a list comprehension, so removing it was super lame. But in the process of translating a comment I wrote in another forum…

In this article[0], Guido says:

> We already have sum(); I'd happily trade reduce() for product(), so that takes care of the two most common uses.

product() never made it in as a builtin; anyone know why?

[0]: http://www.artima.com/weblogs/viewpost.jsp?thread=98196

Re: Functional programming with Python

#47

Earlier quoted context omitted.

If __add__ is taken to be any associative operation of a monoid, then Python's sum is merely equivalent to Haskell's mconcat, which is not nearly as general as reduce/foldl. Plus, unless you implement something akin to newtype wrappers for alternative monoid instances (hardly Pythonic), you're going to be restricted to a single foldable function for any given type. I think you were right the first time; removing redu…

> If __add__ is taken to be any associative operation of a monoid, then Python's sum is merely equivalent to Haskell's mconcat, which is not nearly as general as reduce/foldl I don't have the math to back this up, so please correct me if you know otherwise (and especially if you can demonstrate otherwise, though I know this being wrong is not contingent on that), but: I'm pretty sure `mconcat` with a `map` (or a list…

I don't know much category theory either; my argument is based solely on my knowledge of Haskell.

Maybe I'm being obtuse, but I don't quite understand what you mean when you say "mconcat with a map". But to see why mconcat isn't as general as a fold, you need only to take a look at the types:

    mconcat :: Monoid a => [a] -> a
    foldl :: (a -> b -> a) -> a -> [b] -> a
mconcat is constrained to only operating on monoidal types, whereas foldl has no such constraint. Likewise, mconcat must always yield a result of the same type as the list elements, while foldl is capable of accumulating a result of any type.

mconcat can be implemented in terms of a fold, e.g.

    mconcat = foldl mappend mempty
Such a partially applied foldl is obviously less general than an unapplied foldl, as its first 2 arguments are fixed. There is no way to implement foldl (or even foldl1) in terms of mconcat.

There are plenty of uses for folds that don't involve monoids, and therefore can't be implemented with mconcat plus a Monoid instance. As a trivial example, take

    Prelude> foldl (/) 400 [4, 4, 5]
    5.0
There is no "quotient monoid", because a monoid is defined as a type with an associative binary operation and an identity element with respect to that operation. Division has a right identity (1), but it isn't associative, so it can't be used as a monoidal operation like addition and multiplication can. But foldl still works fine in this case, as the above example shows, where mconcat would not (unless you write a Monoid instance that violates the monoid laws).

Associativity notwithstanding, it's also not adequate to say that 2 monoid instances should be enough for any type. Maybe that is true in some or even most cases, but it is certainly not true in the general case. Haskell has newtypes to mitigate this problem, essentially enabling an arbitrary number of Monoid instances to be defined per type, but Python only lets you write one __add__ and one __mul__ for a given class. On top of that, I don't believe Python even has a product function that folds __mul__ over a list the way sum folds __add__, so really the limit is 1, not 2.

Anyway it's all moot because, as someone pointed out, reduce hasn't been removed from Python, just relegated to a library. :) I think it's a fundamental enough operation that it should be built in, but obviously Guido and I have differing opinions on FP.

Re: Functional programming with Python

#48
post #45

Earlier quoted context omitted.

I'm not sure what you mean. map is not supposed to be an endomorphism, rather it takes a function f and returns a list homomorphism.

I think it'd be nice if it were an endomorphism, though; Making map(f, tuple) -> tuple, map(f, list) -> list, etc. would save users from having to write tuple(map(f, tuple)) (which is something I've done a bunch).

Oh, you just meant you wanted lambda x: map(f,x) to be an endomorphism for any f (you said map itself should be an endomorphism which confused me). Well, it already does what you want: lambda x: map(f,x) takes an iterable and returns an iterable. :)

Re: Functional programming with Python

#49

I'm not sure I agree with "don't write classes". This is Python: classes are the state container. That's the structure Python provides for grouping related pieces of information together, and that's the structure all other Python libraries and programmers expect you to use. Now if you don't want to type the code every time, that's fine: use collections.namedtuple.

A class without methods is just syntactic sugar wrapping around a dictionary of attributes, so in that sense it could be simpler to use the dictionary directly.

The example from the slides was a __str__ method for a data structure, which makes a class the perfect fit in Python. (They did not call it __str__, of course.)

Re: Functional programming with Python

#50
post #45

Earlier quoted context omitted.

I think it'd be nice if it were an endomorphism, though; Making map(f, tuple) -> tuple, map(f, list) -> list, etc. would save users from having to write tuple(map(f, tuple)) (which is something I've done a bunch).

Oh, you just meant you wanted lambda x: map(f,x) to be an endomorphism for any f (you said map itself should be an endomorphism which confused me). Well, it already does what you want: lambda x: map(f,x) takes an iterable and returns an iterable. :)

Fair enough (: I'm not the parent, by the way -- I was just taking a stab at what the parent might mean.
Post reply on HN