Lambda Functions Vs Comprehensions

This article explores Python’s PEP 709, an upgrade in Python 3.12 that enhances the performance of list, set, and dictionary comprehensions. By inlining comprehensions directly into the surrounding code, Python eliminates the need for separate function calls, leading to faster runtime and more efficient memory use. The article also compares comprehensions with lambda functions, examining their pros and cons in data-heavy tasks. With improved speed, reduced CPU load, and a streamlined coding experience, PEP 709’s inlined comprehensions become an ideal choice for efficient data processing, particularly in machine learning and data science workflows.

PYTHON

Jean-Yves TRAN

11/17/20246 min read

What’s a Comprehension?

Comprehensions let you build lists, sets, or dictionaries in a single, concise line of code. For example, instead of writing a traditional loop, you could use a list comprehension like this:

squares = [x**2 for x in range(10)]

This tiny snippet produces a list of squared numbers from 0 to 9 in a single line. Comprehensions are not just for lists—they work for dictionaries and sets too, and they’ve become a Python favorite for their readability and elegance. However, as Python has evolved, so have opportunities to make these comprehensions even more efficient. That’s where PEP 709 comes in.

Introducing PEP 709

PEP 709 brings an upgrade to comprehensions in Python 3.12 by making them faster and less memory-intensive. Usually, comprehensions work like mini-functions within your main code, which keeps them isolated but also makes Python work harder by creating extra memory allocations and function calls each time. With PEP 709, this process becomes smoother and faster.

So, what does this mean for us? Let’s dive in.

How It Works

Current Approach

Today, Python treats each comprehension as if it were its own little function call. This is handy for keeping variables like x (our loop variable) isolated within the comprehension, but it also means extra memory and slower performance, especially if comprehensions are used a lot.

PEP 709’s Solution: Inlining Comprehensions

With PEP 709, Python now "inlines" the comprehension directly into the surrounding code. There’s no separate function for the comprehension anymore, which reduces memory usage and speeds things up. Python still keeps x isolated by temporarily pushing it onto the stack, so it doesn’t interfere with other parts of the code. When the comprehension finishes, Python puts x back to its original state.

The result? Comprehensions are now faster without any loss of isolation. This saves memory and improves performance, making comprehensions even more useful.

Cleaner Tracebacks

Here’s a bonus from PEP 709: cleaner tracebacks. A traceback is what Python shows when there’s an error, detailing each step Python took before it hit the problem. Previously, if an error occurred within a comprehension, Python would show an extra line like <listcomp> in the traceback. This could make it harder to read, especially if there were multiple steps involved.

With the new inlining approach, these extra lines are no longer needed. The traceback now goes directly to the part of the main function where the error happened, keeping it simple and focused. Less clutter in your tracebacks means easier debugging.

Why This Matters for Data Science and Machine Learning

If your work involves data-heavy tasks or parallel processing, these improvements to comprehensions can help streamline performance in subtle but significant ways. Faster comprehensions mean less CPU time spent on memory tracking and more efficient multi-core processing—no extra effort required on your end.

And here’s the best part: no changes to your code are needed to reap these benefits. Simply update to Python 3.12, and the improvements are applied automatically.

Comprehensions vs. Lambda Functions: Two Sides of Python’s Concise Coding Coin

Now, let’s shift gears and talk about another compact Python feature: lambda functions. These “anonymous functions” offer a quick way to create functions without formally defining them. Like comprehensions, lambda functions allow us to streamline our code, often reducing what might be several lines into a single one-liner. But how do they stack up against comprehensions?

A lambda function uses the keyword lambda, takes one or more arguments, and evaluates a single expression.

Two Common Use Cases of Lambda Functions

Lambda functions are most commonly used when paired with functions like map() and filter() for quick transformations or filtering in data.

  • Transforming Data: Doubling each number in a list:

# Doubling a list of numbers using lambda within a map

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

  • Filtering Data: Keeping only the even numbers from a list:

# Filtering even numbers using lambda within a filter

even_numbers = list(filter(lambda x: x % 2 == 0, numbers))

Achieving the Same with Comprehensions

Now let’s rewrite these examples using list comprehensions to see how each approach compares:

  • Transforming Data (List Comprehension for Doubling):


doubled = [x * 2 for x in numbers]

  • Filtering Data: Keeping only the even numbers from a list:


even_numbers = [x for x in numbers if x % 2 == 0]

Pros and Cons of Each Approach

Chaining Lambda Functions

Both lambda functions and comprehensions can be “chained,” or combined sequentially, to perform multiple operations in a single expression. However, each approach has its own quirks and best practices when it comes to chaining.

You can chain lambda functions with functions like map(), filter(), and reduce() to perform multiple transformations or filtering operations in sequence. Here’s how it looks in practice:

Which is Better for Machine Learning and Data Science?

For data science and machine learning, speed and memory efficiency can make a noticeable difference when processing large datasets. Both lambda functions and comprehensions offer quick solutions for data transformation and filtering, but they aren’t always equal when it comes to performance.

  1. Lambda Functions: These can be powerful when paired with functions like map() and filter(), particularly when applying a transformation across a large dataset. However, lambda functions can become harder to read as complexity increases. Additionally, they may have minor performance lags due to the added function call overhead with map() and filter().

  2. List Comprehensions (with PEP 709): Comprehensions are generally faster and more memory-efficient, especially with the recent inlining improvements of PEP 709. This makes them ideal for tasks requiring high-speed data manipulation, such as feature engineering, data filtering, and basic transformations on large datasets. Comprehensions are also more readable, which can be crucial in complex ML workflows where code clarity is key.


from functools import reduce

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

# Example: Doubling each number, filtering even results, and summing them

result = reduce(

lambda acc, x: acc + x,

filter(

lambda x: x % 2 == 0,

map(lambda x: x * 2, numbers)

),

0

)

# Output: 12 (double even numbers: 2, 4, 8, then sum)

Here’s what’s happening:
  1. map(lambda x: x * 2, numbers): Doubles each number.

  2. filter(lambda x: x % 2 == 0, ...): Filters out odd numbers from the doubled values.

  3. reduce(lambda acc, x: acc + x, ...): Sums the remaining values.


Pros and Cons of Chaining Lambda Functions:
  • Pros: Very concise, can apply complex operations in a single line.

  • Cons: Can become harder to read and debug as complexity increases, especially with multiple nested functions.

Chaining Comprehensions

List comprehensions, dictionary comprehensions, and set comprehensions can also be chained or nested. This approach can sometimes be more readable than chaining multiple lambda functions.


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

# Example: Doubling each number, filtering even results, and summing them

result = sum(x 2 for x in numbers if (x 2) % 2 == 0)

# Output: 12 (double even numbers: 2, 4, 8, then sum)

In this example:
  1. The comprehension doubles each number (x * 2 for x in numbers).

  2. It applies a condition to keep only even results (if (x * 2) % 2 == 0).

  3. Finally, it sums the results using sum().

Pros and Cons of Chaining Comprehensions:

  • Pros: Readable, especially for simpler chains; works well with PEP 709 optimizations for added speed.

  • Cons: More complex chains may still require filter() or map() for clarity.

Which one is Better for Chaining?

  • For readability and efficiency (especially in Python 3.12+), comprehensions are usually a better choice when chaining operations.

  • Lambda functions offer flexibility with functions like map(), filter(), and reduce() but can get cumbersome if the operations are nested.

In general, if you find the chain getting too complex with either approach, it’s usually best to break it down into separate steps for readability.

Conclusion

Python’s PEP 709 is a game-changer for those of us who rely on comprehensions for efficient data transformations. With this new inlining approach in Python 3.12, list, dictionary, and set comprehensions now run faster, consume less memory, and create cleaner, simpler tracebacks. These improvements make comprehensions an even more powerful tool in our coding toolkit, especially for data science and machine learning tasks where speed and efficiency are essential.

Comparing comprehensions with lambda functions shows that both have their strengths, but the optimizations of PEP 709 give comprehensions an edge in performance and readability. This is especially clear when chaining operations. Lambda functions can be chained using map(), filter(), and reduce(), but readability often suffers. Comprehensions, on the other hand, allow for more intuitive and readable chaining, especially with the performance boost of inlining.

For Python users who want code that’s both clean and fast, comprehensions, backed by PEP 709, are the clear winner. Whether you’re processing data, performing calculations, or transforming lists, Python 3.12’s enhanced comprehensions provide the flexibility and speed to handle it all—without requiring any extra work on your part. So, if you haven’t yet, now’s the perfect time to upgrade and start making the most of these optimizations!

SOURCES:

PEP 709 – Inlined comprehensions
https://peps.python.org/pep-0709/