How to Use Lambda Functions in Python

Written by Brendon
29 April 2025

Learn how to use lambda functions in Python effectively, from syntax and higher-order functions to debugging tips and best practices.

Lambda functions in Python

Introduction #

Lambda functions are one of those features in Python that seem simple at first glance — and mostly, they are. They're a way to create small, anonymous functions quickly, without the ceremony of a full def block.

But like many things in Python, a little feature can open up a lot of nuance.

In this post, we’ll break down exactly what lambda functions are, why they exist, and how they fit into Python’s flexible approach to functions. We'll also look at how lambdas behave under the hood, when they make your code better, and when you're probably better off using a regular named function.

By the end, you’ll know how to wield lambdas thoughtfully — using them when they help and skipping them when they don’t.

Let’s take a closer look at Python’s anonymous functions and what they bring to the table.


By the way, here's the video version of this article. Perfect if you want a short, high level overview before diving into this more in depth article.


What Are Lambda Functions in Python? #

At their core, lambda functions are a way to define small, throwaway functions without giving them a name. They’re meant for quick, simple operations — the kinds of functions you don’t need to reference anywhere else in your code.

The basic syntax looks like this:

lambda arguments: expression

A lambda function can take any number of arguments, but it must consist of a single expression. There's no return statement needed — Python automatically returns the result of the expression.

Here’s a simple example:

double = lambda x: x * 2
print(double(4))  # Output: 8

In this case, double is a variable that points to a function that doubles whatever you give it. It works just like a regular function created with def, but without the formal definition.

Important constraint

Lambda functions are limited to a single expression. You can't sneak in multiple lines, assignments, or complex branching. If you find yourself trying to cram too much into a lambda, it’s a sign you probably need a full function instead.

Why Does Python Even Have Lambda Functions? #

Python’s philosophy leans heavily on readability — so why introduce something that seems a little cryptic?

Because sometimes, less is more.

Lambdas are particularly useful when you need a quick function to pass into another function, especially higher-order functions like map(), filter(), and sorted(). They let you define simple behavior right at the point of use, without cluttering up your code with a bunch of tiny named functions.

In other words: lambdas aren’t trying to replace regular functions. They're just a convenient shortcut when the function you're creating is short, obvious, and used once.

The Anonymous Nature of Lambdas (and How They Fit into Python’s Namespace) #

One of the defining traits of a lambda function is that it’s anonymous. When you create a lambda, you’re making a function that doesn’t have a formal name — unless you assign it to a variable yourself.

This is different from the usual def-defined function, where naming is part of the package:

def add(x, y):
    return x + y

# Named function
print(add.__name__)  # Output: add

Compare that to a lambda:

add_lambda = lambda x, y: x + y

print(add_lambda.__name__)  # Output: <lambda>

Notice that Python automatically gives it the name lambda behind the scenes — which, as you might guess, isn’t particularly helpful when you’re trying to debug.

What Does "Anonymous" Really Mean? #

When we say a lambda is anonymous, we’re pointing out that it doesn’t have a unique, descriptive name associated with it inside Python's internal namespace. Instead, it’s treated like any other object — just one that happens to be a function.

You can still assign a lambda to a variable, pass it to another function, or store it in a list. It’s a first-class object like everything else in Python. But unless you explicitly bind it to a variable, it remains a sort of floating function without a direct label.

Quick example:

# A lambda passed directly to sorted()
sorted_numbers = sorted([5, 2, 9, 1], key=lambda x: -x)

Here, the lambda exists just long enough to tell sorted() how to order the numbers, and then it quietly disappears.

No name, no fuss.

Why Does This Matter? #

The fact that lambdas are anonymous has two important side effects:

  1. Debugging Gets Trickier

When something goes wrong inside a lambda, the traceback will point you to a mysterious lambda function. This can make it harder to track down issues compared to a regular named function.

  1. Readability Can Suffer

When you see a def clean_up_string(s): in code, you have a pretty good idea of what it does. When you see lambda s: s.strip().lower(), you have to read the entire expression to understand it. Small lambdas are fine, but anything more complicated can quickly hurt readability.

This is why lambda functions work best for small, obvious tasks — things that a reader can understand at a glance without needing a name to explain their purpose.

Lambdas Are First-Class Citizens (Just Like Regular Functions) #

In Python, functions are first-class objects. That’s a fancy way of saying: functions can be passed around just like integers, strings, or any other object. You can assign them to variables, store them in data structures, pass them as arguments, and return them from other functions.

Lambda functions are no exception.

Even though they’re anonymous, lambdas are full-fledged function objects under the hood — just a bit more compact.

Assigning a Lambda to a Variable #

Yes, you can give a lambda function a name by assigning it to a variable:

square = lambda x: x ** 2
print(square(4))  # Output: 16

But here’s the thing: if you're going to assign a lambda to a variable, you might as well use def. It’s clearer, easier to debug, and more Pythonic.

def square(x):
    return x ** 2

Same result, better readability. The def version will also show up in tracebacks as square() rather than the ever-mysterious lambda.

So while assigning lambdas to variables is valid Python, it’s generally discouraged unless you’re doing something very specific — like creating a quick, local function inside another function or expression.

Passing Lambdas as Arguments #

This is where lambdas really shine. Instead of defining a separate helper function, you can define a tiny one liner exactly where you need it.

numbers = [1, 2, 3, 4, 5]

# Use lambda to double each number
doubled = list(map(lambda x: x * 2, numbers))

print(doubled)  # Output: [2, 4, 6, 8, 10]

There’s no need to define a one-off def double(x) just to use it once inside map() — the lambda keeps things neat and in context.

This kind of inline usage is where lambdas are most useful: small operations passed as arguments to functions like map(), filter(), sorted(), or even custom higher-order functions.

Returning a Lambda from a Function #

Just like any other function object, you can return a lambda from another function — and this opens the door to some interesting patterns, like closures and function factories.

def multiplier(n):
    return lambda x: x * n

double = multiplier(2)
triple = multiplier(3)

print(double(5))  # Output: 10
print(triple(5))  # Output: 15

Here, the lambda “remembers” the value of n even after multiplier() has returned. This is a closure — and we’ll explore this kind of usage more in the next section.

Using Lambdas in Higher-Order Functions #

Now that we know lambdas are lightweight, anonymous function objects, it’s time to see them in action. One of their most common roles is working with higher-order functions — that is, functions that accept other functions as arguments.

In Python, a few higher-order functions show up again and again: map(), filter(), and sorted(). Lambdas fit into these perfectly, helping you define small, throwaway behavior exactly where you need it.

Let’s look at each one in action.

map() — Applying a Function to Every Item #

map() takes a function and an iterable, applies the function to every item, and returns a new iterator.

Without lambdas:

def square(x):
    return x ** 2

numbers = [1, 2, 3, 4]
squared = list(map(square, numbers))

With a lambda:

numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x ** 2, numbers))

Both versions work the same way.

The lambda version skips the overhead of defining a named square() function when you just need it once.

filter() — Keeping Only What Matches a Condition #

filter() also takes a function and an iterable, but instead of transforming every item, it keeps only the items where the function returns True.

Example without lambdas:

def is_even(x):
    return x % 2 == 0

numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(is_even, numbers))

Now with a lambda:

numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers))

Again: short, clear, inline logic. Perfect lambda territory.

sorted() — Customizing How Things Are Ordered #

When you want to sort something based on custom criteria, lambdas make it easy to define a key function on the fly.

Suppose you have a list of words and you want to sort them by length:

words = ["banana", "pie", "Washington", "book"]

# Sort by length
sorted_words = sorted(words, key=lambda word: len(word))
print(sorted_words)
# Output: ['pie', 'book', 'banana', 'Washington']

Without the lambda, you’d have to create a separate def word_length(word): return len(word) function — which feels like overkill for such a simple rule.

A Quick Note on Readability #

Lambdas are great in these cases because the logic is short and obvious.

But if your lambda is starting to grow multiple conditions, nested expressions, or anything even slightly confusing, it's better to switch to a regular named function.

When in doubt, optimize for the person who has to read your code six months from now (which, let's be honest, is probably future-you).

When to Use Lambdas vs Normal Functions #

Lambdas are elegant when used well — and a mess when they’re not.

So how do you know when it’s appropriate to use a lambda, and when you should just write a regular function with def? It comes down to context, complexity, and clarity.

Let’s break it down.

✅ Use a Lambda When... #

  • The function is short and simple. Ideally one or two operations, like x: x.lower() or n: n % 10.
  • You’re passing it immediately to another function. Lambdas shine when used inline with map(), filter(), sorted(), or custom higher-order functions.
  • You don’t need to reuse it elsewhere.

Lambdas are best for one-off jobs. If you’re repeating logic, give it a proper name.

❌ Avoid Lambdas When... #

  • The logic is more than one expression.
  • Python won’t let you add multiple lines or statements to a lambda — and trying to cram it all into one expression leads to unreadable code.
  • You care about meaningful tracebacks or debugging. Lambdas show up as lambda in errors, logs, and tracebacks — not super helpful when you’re tracking down a bug.
  • You’re assigning it to a variable. Technically allowed, but if you're naming a lambda, you're already halfway to writing a proper function. Just use def.
# This works...
shout = lambda s: s.upper()

# ...but this is better
def shout(s):
    return s.upper()

Rule of Thumb #

If a lambda makes your code clearer, use it. If it makes your code clever, pause and rethink it.

Simple lambdas are Pythonic. Over engineered ones are just confusing.

Returning Lambdas (and a Quick Peek at Closures) #

So far, we’ve mostly seen lambdas passed into functions like map() and sorted().

But you can also return a lambda from a function — and when you do, you’re stepping into the world of closures.

Sounds fancy, but the basic idea is simple: a closure is a function that “remembers” values from its surrounding scope, even after that scope has finished executing.

Let’s see it in action.

Building a Power Function Factory #

Suppose you want to create functions that raise numbers to different powers — like squaring or cubing — without having to write separate square() and cube() functions manually.

You can write a factory function like this:

def to_power_of(n):
    return lambda a: a ** n

Here’s how it works:

squared = to_power_of(2)
cubed = to_power_of(3)

print(squared(5))  # Output: 25
print(cubed(5))    # Output: 125

Each time you call to_power_of(), it returns a new lambda that knows what n was at the time you created it. Even though to_power_of() is long gone from the call stack, the lambda still “remembers” the value of n.

That’s a closure in action — no wizardry, just Python keeping references around under the hood.

Why This Matters #

Closures are a powerful (and often underused) tool for creating flexible, reusable pieces of behavior without a lot of boilerplate.

They’re especially handy when:

  • You want to customize a small function with specific behavior.
  • You’re using functional programming techniques like currying or partial application.
  • You’re building decorators or higher-order functions.

And because lambdas are so lightweight, they’re often the perfect way to define a closure without overcomplicating things.

Next, we’ll take a deeper look under the hood by disassembling a lambda and a normal function with Python’s dis module — and see just how similar (and different) they really are.

Under the Hood: Lambdas vs Named Functions (with dis) #

By now, it’s clear that lambda functions behave a lot like regular functions. But what exactly is the difference under the hood?

Let’s take a quick detour into Python bytecode using the dis module — a built-in tool that disassembles Python code into its low-level operations. It’s not something you’ll need every day, but it’s a useful way to see what’s really going on when Python runs your code.

Comparing a Named Function and a Lambda #

Let’s start with two functions that do the exact same thing:

def add(a, b):
    return a + b

add_lambda = lambda a, b: a + b

Now disassemble both:

import dis

dis.dis(add)
dis.dis(add_lambda)

You’ll see something more or less like this depending on the computer you're using to run it:

  1           RESUME                   0

  2           LOAD_FAST_LOAD_FAST      1 (a, b)
              BINARY_OP                0 (+)
              RETURN_VALUE
  4           RESUME                   0
              LOAD_FAST_LOAD_FAST      1 (a, b)
              BINARY_OP                0 (+)
              RETURN_VALUE

Surprise: the bytecode for both functions is almost identical.

That’s because lambda is just syntactic sugar — a compact way to define a function. Once defined, it behaves like any other function object.

Naming and Debugging #

Where lambdas do differ is in their metadata — specifically, their name. We saw this earlier in this article but let's dig a little deeper now.

Check this out:

print(add.__name__)        # 'add'
print(add_lambda.__name__)  # '<lambda>'

Named functions carry their name around, which is helpful in tracebacks, logging, and debugging.

Lambdas, on the other hand, are anonymous. They’ll always show up as lambda in error messages and logs, which can make tracking down bugs a bit more frustrating.

For example, if something goes wrong deep inside a lambda, the traceback might look like:

TypeError: <lambda>() missing 1 required positional argument: 'b'

Which tells you... almost nothing.

Real World Use Cases for Lambda Functions #

So far, we’ve looked at what lambdas are and how they work — now let’s see them in action with some real-world problems you might actually care about.

These examples highlight exactly where lambdas earn their keep: quick transformations, clean sorting, and lightweight filtering.

Sorting a List of Dictionaries by a Field #

Let’s say you’ve got a list of users and want to sort them by their score. A lambda keeps it clean and local:

users = [
    {"name": "Alice", "score": 82},
    {"name": "Bob", "score": 91},
    {"name": "Charlie", "score": 78}
]

sorted_users = sorted(users, key=lambda user: user["score"])
print(sorted_users)

Output:

[{'name': 'Charlie', 'score': 78},
 {'name': 'Alice', 'score': 82},
 {'name': 'Bob', 'score': 91}]

No helper function necessary — the lambda gets straight to the point.

Filtering Product Names That Are On Sale #

Working with product names and want to filter out just the ones with “sale” in the name? This is a perfect one-liner with filter():

products = ["Winter Jacket", "Summer Sale Shoes", "Clearance Sale T-Shirts", "Regular Socks"]

on_sale = list(filter(lambda name: "sale" in name.lower(), products))
print(on_sale)

Output:

['Summer Sale Shoes', 'Clearance Sale T-Shirts']

It’s case-insensitive, compact, and avoids the ceremony of writing a whole def is_on_sale() function.

Generating Custom Greetings #

Suppose you’re greeting users and want to lowercase their names for consistency. No need to overthink it:

names = ["ALICE", "Bob", "ChArLiE"]

greetings = list(map(lambda name: f"Hello, {name.lower()}!", names))
print(greetings)

Output:

['Hello, alice!', 'Hello, bob!', 'Hello, charlie!']

Quick transformation, no clutter. Exactly what lambdas were made for.

Wrapping Up #

Lambda functions are one of those Python features that seem small but open up a surprising amount of flexibility. Used thoughtfully, they can make your code cleaner, faster to write, and easier to read — provided you keep them simple.

The real trick is knowing when brevity adds clarity — and when it just adds confusion.

As a rule of thumb:

If you can explain what a lambda does in a short sentence without needing to pause, you’re probably using it well.

Now you’ve got a solid handle on what lambdas are, how they work under the hood, when to use them, and when to reach for a good old-fashioned def block instead.

Happy coding — and may your anonymous functions stay helpful and your tracebacks easy to read.