[ Team LiB ] |
14.1 Anonymous Functions: lambdaSo 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.
14.1.1 lambda ExpressionsThe 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:
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 CodeThe 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]
>>> ((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 Scopeslambdas 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.
|
[ Team LiB ] |