Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p><strong>Edit</strong>: I've been taken to task in the comments (in part, it seems, by fanatics of FP in Python, but not exclusively) for not providing more explanations/examples, so, expanding the answer to supply some.</p> <p><code>lambda</code>, even more so <code>map</code> (and <code>filter</code>), <strong>and</strong> most especially <code>reduce</code>, are hardly ever the right tool for the job in Python, which is a strongly multi-paradigm language.</p> <p><code>lambda</code> main advantage (?) compared to the normal <code>def</code> statement is that it makes an <strong>anonymous</strong> function, while <code>def</code> gives the function a name -- and for that very dubious advantage you pay an enormous price (the function's body is limited to one expression, the resulting function object is not pickleable, the very lack of a name sometimes makes it much harder to understand a stack trace or otherwise debug a problem -- need I go on?!-).</p> <p>Consider what's probably the single most idiotic idiom you sometimes see used in "Python" (Python with "scare quotes", because it's obviously <strong>not</strong> idiomatic Python -- it's a bad transliteration from idiomatic Scheme or the like, just like the more frequent overuse of OOP in Python is a bad transliteration from Java or the like):</p> <pre><code>inc = lambda x: x + 1 </code></pre> <p>by assigning the lambda to a name, this approach immediately throws away the above-mentioned "advantage" -- and doesn't lose any of the DISadvantages! For example, <code>inc</code> doesn't <strong>know</strong> its name -- <code>inc.__name__</code> is the useless string <code>'&lt;lambda&gt;'</code> -- good luck understanding a stack trace with a few of these;-). The proper Python way to achieve the desired semantics in this simple case is, of course:</p> <pre><code>def inc(x): return x + 1 </code></pre> <p><strong>Now</strong> <code>inc.__name__</code> is the string <code>'inc'</code>, as it clearly should be, and the object is pickleable -- the semantics are otherwise identical (in this simple case where the desired functionality fits comfortably in a simple expression -- <code>def</code> also makes it trivially easy to refactor if you need to temporarily or permanently insert statements such as <code>print</code> or <code>raise</code>, of course).</p> <p><code>lambda</code> is (part of) an expression while <code>def</code> is (part of) a statement -- that's the one bit of syntax sugar that makes people use <code>lambda</code> sometimes. Many FP enthusiasts (just as many OOP and procedural fans) dislike Python's reasonably strong distinction between expressions and statements (part of a general stance towards <a href="http://en.wikipedia.org/wiki/Command-query_separation" rel="noreferrer">Command-Query Separation</a>). Me, I think that when you use a language you're best off using it "with the grain" -- the way it was <strong>designed</strong> to be used -- rather than fighting against it; so I program Python in a Pythonic way, Scheme in a Schematic (;-) way, Fortran in a Fortesque (?) way, and so on:-).</p> <p>Moving on to <code>reduce</code> -- one comment claims that <code>reduce</code> is the best way to compute the product of a list. Oh, really? Let's see...:</p> <pre><code>$ python -mtimeit -s'L=range(12,52)' 'reduce(lambda x,y: x*y, L, 1)' 100000 loops, best of 3: 18.3 usec per loop $ python -mtimeit -s'L=range(12,52)' 'p=1' 'for x in L: p*=x' 100000 loops, best of 3: 10.5 usec per loop </code></pre> <p>so the simple, elementary, trivial loop is about twice as fast (as well as more concise) than the "best way" to perform the task?-) I guess the advantages of speed and conciseness must therefore make the trivial loop the "bestest" way, right?-)</p> <p>By further sacrificing compactness and readability...:</p> <pre><code>$ python -mtimeit -s'import operator; L=range(12,52)' 'reduce(operator.mul, L, 1)' 100000 loops, best of 3: 10.7 usec per loop </code></pre> <p>...we can get <strong>almost</strong> back to the easily obtained performance of the simplest and most obvious, compact, and readable approach (the simple, elementary, trivial loop). This points out another problem with <code>lambda</code>, actually: performance! For sufficiently simple operations, such as multiplication, the overhead of a function call is quite significant compared to the actual operation being performed -- <code>reduce</code> (and <code>map</code> and <code>filter</code>) often forces you to insert such a function call where simple loops, list comprehensions, and generator expressions, allow the readability, compactness, and speed of in-line operations.</p> <p>Perhaps even worse than the above-berated "assign a lambda to a name" anti-idiom is actually the following anti-idiom, e.g. to sort a list of strings by their lengths:</p> <pre><code>thelist.sort(key=lambda s: len(s)) </code></pre> <p>instead of the obvious, readable, compact, speedier</p> <pre><code>thelist.sort(key=len) </code></pre> <p>Here, the use of <code>lambda</code> is doing nothing but inserting a level of indirection -- with no good effect whatsoever, and plenty of bad ones.</p> <p>The motivation for using <code>lambda</code> is often to allow the use of <code>map</code> and <code>filter</code> instead of a vastly preferable loop or list comprehension that would let you do plain, normal computations in line; you still pay that "level of indirection", of course. It's not Pythonic to have to wonder "should I use a listcomp or a map here": just always use listcomps, when both appear applicable and you don't know which one to choose, on the basis of "there should be one, and preferably only one, obvious way to do something". You'll often write listcomps that could not be sensibly translated to a map (nested loops, <code>if</code> clauses, etc), while there's no call to <code>map</code> that can't be sensibly rewritten as a listcomp.</p> <p>Perfectly proper functional approaches in Python often include list comprehensions, generator expressions, <code>itertools</code>, higher-order functions, first-order functions in various guises, closures, generators (and occasionally other kinds of iterators).</p> <p><code>itertools</code>, as a commenter pointed out, does include <code>imap</code> and <code>ifilter</code>: the difference is that, like all of itertools, these are stream-based (like <code>map</code> and <code>filter</code> builtins in Python 3, but differently from those builtins in Python 2). <code>itertools</code> offers a set of building blocks that compose well with each other, and splendid performance: especially if you find yourself potentially dealing with very long (or even unbounded!-) sequences, you owe it to yourself to become familiar with itertools -- their whole <a href="http://docs.python.org/library/itertools.html" rel="noreferrer">chapter</a> in the docs makes for good reading, and the <a href="http://docs.python.org/library/itertools.html?highlight=itertools#recipes" rel="noreferrer">recipes</a> in particular are quite instructive.</p> <p>Writing your own higher-order functions is often useful, especially when they're suitable for use as <a href="http://docs.python.org/reference/compound_stmts.html#function" rel="noreferrer">decorators</a> (both function decorators, as explained in that part of the docs, and class decorators, introduced in Python 2.6). Do remember to use <a href="http://docs.python.org/library/functools.html?highlight=decorators#functools.wraps" rel="noreferrer">functools.wraps</a> on your function decorators (to keep the metadata of the function getting wrapped)!</p> <p>So, summarizing...: anything you can code with <code>lambda</code>, <code>map</code>, and <code>filter</code>, you can code (more often than not advantageously) with <code>def</code> (named functions) and listcomps -- and usually moving up one notch to generators, generator expressions, or <code>itertools</code>, is even better. <code>reduce</code> meets the legal definition of "attractive nuisance"...: it's <strong>hardly ever</strong> the right tool for the job (that's why it's not a built-in any more in Python 3, at long last!-).</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload