Python Lambda Arguments: Syntax and Pitfalls
python lambda arguments: Learn how Python lambda arguments work: syntax, multiple parameters, defaults, keyword arguments, closures, and the late-binding pitfall.
A lambda in Python is an expression that produces a small anonymous function. Its argument list follows the same rules as a def statement's parameter list, but the body is restricted to a single expression whose value is returned implicitly. Understanding python lambda arguments means knowing which parameter forms are allowed, how they bind at call time, and where the behavior diverges from a regular function.
Lambda Syntax: Where Arguments Fit
The general form of a lambda is:
lambda arguments: expression
The arguments portion accepts the same parameter syntax as a def, including positional parameters, defaults, *args, and **kwargs. The expression is evaluated when the lambda is called, and its result becomes the return value.
double = lambda x: x * 2 print(double(21)) # 42
The name double is bound to the function object created by the lambda. The parameter x is local to that function; it does not leak into the surrounding scope.
Multiple Arguments and Positional Order
Lambdas accept several positional arguments separated by commas:
add = lambda a, b: a + b print(add(3, 4)) # 7
Positional binding works exactly as it does in a def: the first value passed maps to the first parameter, the second to the second, and so on. Passing too few or too many arguments raises a TypeError at call time.
Default Arguments in Lambdas
Default values are supported:
greet = lambda name, greeting="Hello": f"{greeting}, {name}!" print(greet("Ada")) # Hello, Ada! print(greet("Ada", "Hi")) # Hi, Ada!
Defaults are evaluated once, when the lambda is defined, not on each call. If the default is a mutable object such as a list or dictionary, that same object is shared across all calls. Mutating it in one call affects every later call, which is a common source of subtle bugs.
Keyword Arguments
Lambdas accept keyword arguments when the caller supplies them:
def apply(func, **kwargs): return func(**kwargs) result = apply(lambda x, y=10: x + y, x=5, y=20) print(result) # 25
The parameter names declared in the lambda determine which keyword names are valid. Calling apply(lambda x: x, y=5) raises a TypeError because the lambda has no parameter named y.
Variable-Length Arguments
A lambda can accept an arbitrary number of positional or keyword arguments:
sum_all = lambda *args: sum(args) print(sum_all(1, 2, 3, 4)) # 10 config = lambda **kwargs: sorted(kwargs.items()) print(config(host="db", port=5432)) # [('host', 'db'), ('port', 5432)]
This is useful when a lambda acts as a thin wrapper that forwards arguments to another function. The *args tuple and **kwargs dict behave exactly as they do in a def.
Using Lambda Arguments with Higher-Order Functions
The most common practical use is passing a lambda as the key argument to sorted, max, min, or as the function passed to map and filter:
items = [("apple", 3), ("banana", 1), ("cherry", 2)] items.sort(key=lambda item: item[1]) print(items) # [('banana', 1), ('cherry', 2), ('apple', 3)]
The lambda receives one element of the sequence and returns the value to compare. Keeping the lambda short here preserves readability; a longer body would be clearer as a named function.
The Late-Binding Closure Pitfall
A lambda that references a variable from an enclosing scope captures the variable itself, not its value at definition time. When the lambda is called later, it reads the variable's current value.
funcs = [lambda: i for i in range(3)] print([f() for f in funcs]) # [2, 2, 2]
All three lambdas return 2 because they all reference the same loop variable i, which has reached its final value by the time the lambdas run. The standard fix binds the current value as a default argument:
funcs = [lambda i=i: i for i in range(3)] print([f() for f in funcs]) # [0, 1, 2]
The default argument captures the value of i at definition time, so each lambda returns a distinct number.
Runtime Cost and Readability Tradeoffs
A lambda is not faster than an equivalent def. The generated bytecode is nearly identical, and the function-call overhead dominates either way. The tradeoff is readability: a lambda can hold only one expression, so complex logic becomes dense and difficult to debug.
Prefer a def when the body needs more than one expression, when a docstring is required, or when the same logic appears in several places. A lambda is appropriate when the logic is short, used once, and the surrounding call reads clearly with the function inline.
Compatibility Notes
Lambda syntax has been stable across Python 3.x, and the closure and default-argument semantics described here apply consistently. Code that relies on print inside a lambda, such as lambda x: print(x), works only in Python 3 because print is a statement in Python 2. If you maintain code that must run on both, keep the body free of print and other statement-only constructs.