14.1 Anonymous Functions: lambda

So far, we've seen what it takes to write our own functions in Python. The next sections turn to a few more advanced function-related ideas. Most of these are optional features, but can simplify your coding tasks when used well.

Besides the def statement, Python also provides an expression form that generates function objects. Because of its similarity to a tool in the LISP language, it's called lambda.[1] Like def, this expression creates a function to be called later, but returns it instead of assigning it to a name. This is why lambdas are sometimes known as anonymous (i.e., unnamed) functions. In practice, they are often used as a way to inline a function definition, or defer execution of a piece of code.

[1] The name "lambda" seems to scare people more than it should. It comes from Lisp, which got it from the lambda calculus, which is a form of symbolic logic. In Python, though, it's really just a keyword that introduces the expression syntactically.

14.1.1 lambda Expressions

The lambda's general form is the keyword lambda, followed by one or more arguments (exactly like the arguments list you enclose in parenthesis in a def header), followed by an expression after a colon:

lambda argument1, argument2,... argumentN : expression using arguments

Function objects returned by running lambda expressions work exactly the same as those created and assigned by def. But the lambda has a few differences that make it useful in specialized roles:

  • lambda is an expression, not a statement. Because of this, a lambda can appear in places a def is not allowed by Python's syntax inside a list literal or function call, for example. As an expression, the lambda returns a value (a new function), which can be assigned a name optionally; the def statement always assigns the new function to the name in the header, instead of returning it as a result.

  • lambda bodies are a single expression, not a block of statements. The lambda's body is similar to what you'd put in a def body's return statement; simply type the result as a naked expression, instead of explicitly returning it. Because it is limited to an expression, lambda is less general than a def; you can only squeeze so much logic into a lambda body without using statements such as if (read on for more on this). This is by design, to limit program nesting: lambda is designed for coding simple functions, and def handles larger tasks.

Apart from those distinctions, the def and lambda do the same sort of work. For instance, we've seen how to make functions with def statements:

>>> def func(x, y, z): return x + y + z ... >>> func(2, 3, 4) 9

But you can achieve the same effect with a lambda expression, by explicitly assigning its result to a name through which you can later call:

>>> f = lambda x, y, z: x + y + z >>> f(2, 3, 4) 9

Here, f is assigned the function object the lambda expression creates; this is how def works too, but its assignment is automatic. Defaults work on lambda arguments, just like the def:

>>> x = (lambda a="fee", b="fie", c="foe": a + b + c) >>> x("wee") 'weefiefoe'

The code in a lambda body also follows the same scope lookup rules as code inside a def lambda expressions introduce a local scope much like a nested def, which automatically sees names in enclosing functions, the module, and the built-in scope (via the LEGB rule):

>>> def knights(  ): ...     title = 'Sir' ...     action = (lambda x: title + ' ' + x)   # Title in enclosing def ...     return action                          # Return a function. ... >>> act = knights(  ) >>> act('robin') 'Sir robin'

Prior to Release 2.2, the value for name title would typically be passed in as a default argument value instead; flip back to the scopes coverage of Chapter 13 if you've forgotten why.

14.1.2 Why lambda?

Generally speaking, lambdas come in handy as a sort of function shorthand that allows you to embed a function's definition within the code that uses it. They are entirely optional (you can always use def instead), but tend to be a simpler coding construct in scenarios when you just need to embed small bits of executable code.

For instance, we'll see later that callback handlers are frequently coded as in-line lambda expressions embedded directly in a registration call's arguments list, instead of being defined with a def elsewhere in a file and referenced by name (see the callbacks sidebar for an example).

lambdas are also commonly used to code jump tables lists or dictionaries of actions to be performed on demand. For example:

L = [(lambda x: x**2), (lambda x: x**3), (lambda x: x**4)] for f in L:     print f(2)     # Prints 4, 8, 16 print L[0](3)      # Prints 9

The lambda expression is most useful as a shorthand for def, when you need to stuff small pieces of executable code in places where statements are illegal syntactically. This code snippet, for example, builds up a list of three functions by embedding lambda expressions inside a list literal; def won't work inside a list literal like this, because it is a statement, not an expression.

You can do the same sort of thing with dictionaries and other data structures in Python, to build up action tables:

>>> key = 'got' >>> {'already': (lambda: 2 + 2), ...  'got':     (lambda: 2 * 4), ...  'one':     (lambda: 2 ** 6) ... }[key](  ) 8

Here, when Python makes the dictionary, each of the nested lambdas generates and leaves behind a function to be called later; indexing by key fetches one of those functions, and parenthesis force the fetched function to be called. When coded this way, a dictionary becomes a more general multiway branching tool than what we could show you in Chapter 9s coverage of if statements.

To make this work without lambda, you'd need to instead code three def statements somewhere else in your file, and outside the dictionary in which the functions are to be used:

def f1(  ): ... def f2(  ): ... def f3(  ): ... ... key = ... {'already': f1, 'got': f2, 'one': f3}[key](  )

This works too, and avoids lambdas; but your defs may be arbitrarily far away in your file, even if they are just little bits of code. The code proximity that lambdas provide is especially useful for functions that will only be used in a single context if the three functions here are not useful anywhere else, it makes sense to embed their definition within the dictionary as lambdas.

lambdas also come in handy in function argument lists, as a way to inline temporary function definitions not used anywhere else in your program; we'll meet examples of such other uses later in this chapter when we study map.

14.1.3 How (Not) to Obfuscate Your Python Code

The fact that the body of a lambda has to be a single expression (not statements) would seem to place severe limits on how much logic you can pack into a lambda. If you know what you're doing, though, you can code almost every statement in Python as an expression-based equivalent.

For example, if you want to print from the body of a lambda function, simply say sys.stdout.write(str(x)+'\n'), instead of print x. (See Chapter 8 if you've forgotten why.) Similarly, it's possible to emulate an if statement by combining Boolean operators in expressions. The expression:

((a and b) or c)

is roughly equivalent to:

if a:     b else:     c

and is almost Python's equivalent to the C language's a?b:c ternary operator. (To understand why, you need to have read the discussion of Boolean operators in Chapter 9.) In short, Python's and and or short-circuit (they don't evaluate the right side, if the left determines the result), and always return either the value on the left, or the value on the right. In code:

>>> t, f = 1, 0 >>> x, y = 88, 99 >>> a = (t and x) or y           # If true, x >>> a 88 >>> a = (f and x) or y           # If false, y >>> a 99

This works, but only as long as you can be sure that x will not be false too (otherwise, you will always get y). To truly emulate an if statement in an expression, you must wrap the two possible results so as to make them non-false, and then index to pull out the result at the end:[2]

[2] As we write this, a debate rages on comp.lang.python about adding a more direct ternary conditional expression to Python; see future release notes for new developments on this front. Note that you can almost achieve the same effect as the and/or with an expression ((falsevalue,truevalue)[condition]), except that this does not short-circuit (both possible results are evaluated every time), and the condition must be 0 or 1.

>>> ((t and [x]) or [y])[0]     # If true, x 88 >>> ((f and [x]) or [y])[0]     # If false, y 99 >>> (t and f) or y              # Fails: f is false, skipped 99 >>> ((t and [f]) or [y])[0]     # Works: f returned anyhow 0

Once you've muddled through typing this a few times, you'll probably want to wrap it for reuse:

>>> def ifelse(a, b, c): return ((a and [b]) or [c])[0] ... >>> ifelse(1, 'spam', 'ni') 'spam' >>> ifelse(0, 'spam', 'ni') 'ni'

Of course, you can get the same results by using an if statement here instead:

def ifelse(a, b, c):    if a: return b    else: return c

But expressions like these can be placed inside a lambda, to implement selection logic:

>>> lower = (lambda x, y: (((x < y) and [x]) or [y])[0]) >>> lower('bb', 'aa') 'aa' >>> lower('aa', 'bb') 'aa'

Finally, if you need to perform loops within a lambda, you can also embed things like map calls and list comprehension expressions tools we'll meet later in this section:

>>> import sys >>> showall = (lambda x: map(sys.stdout.write, x)) >>> t = showall(['spam\n', 'toast\n', 'eggs\n']) spam toast eggs

But now that we've shown you these tricks, we need ask you to please only use them as a last resort. Without due care, they can lead to unreadable (a.k.a. obfuscated) Python code. In general, simple is better than complex, explicit is better than implicit, and full statements are better than arcane expressions. On the other hand, you may find these useful, when taken in moderation.

14.1.4 Nested lambdas and Scopes

lambdas are the main beneficiaries of nested function scope lookup (the E in the LEGB rule). In the following, for example, the lambda appears inside a def the typical case and so can access the value that name x had in the enclosing function's scope, at the time that the enclosing function was called:

>>> def action(x): ...     return (lambda y: x + y)        # Make, return function. >>> act = action(99) >>> act <function <lambda> at 0x00A16A88> >>> act(2) 101

What we didn't illustrate in the prior discussion is that a lambda also has access to the names in any enclosing lambda. This case is somewhat obscure, but imagine if we recoded the prior def with a lambda:

>>> action = (lambda x: (lambda y: x + y)) >>> act = action(99) >>> act(3) 102 >>> ((lambda x: (lambda y: x + y))(99))(4) 103

Here, the nested lambda structure makes a function that makes a function when called. In both cases, the nested lambda's code has access to variable x in the enclosing lambda. This works, but it's fairly convoluted code; in the interest of readability, nested lambdas are generally best avoided.

Why You Will Care: Callbacks

Another very common application of lambda is to define inline callback functions for the Tkinter GUI API. For example, the following creates a button that prints a message on the console when pressed:

import sys x = Button(         text ='Press me',         command=(lambda:sys.stdout.write('Spam\n')))

Here, the callback handler is registered by passing a function generated with a lambda to the command keyword argument. The advantage of lambda over def here, is that the code that handles a button press is right here embedded in the button creation call.

In effect, the lambda defers execution of the handler until the event occurs: the write call happens on button presses, not when the button is created. Because the nested function scope rules apply to lambdas as well, they are also easier to use as callback handlers (as of Python 2.2) they automatically see names in the function in which they are coded, and no longer require passed-in defaults. This is especially handy for accessing the special self instance argument that is a local variable in enclosing class method functions:

class MyGui:     def makewidgets(self):         Button(command=(lambda: self.display("spam")))     def display(self, message):         ...use message...

In prior releases, even self had to be passed in with defaults. More on classes in Part VI, and more on Tkinter in Part VIII.




Learning Python
Learning Python: Powerful Object-Oriented Programming
ISBN: 0596158068
EAN: 2147483647
Year: 2003
Pages: 253
Authors: Mark Lutz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net