DEV Community

Cover image for Python Lambda Functions - What The?
Lachlan Eagling
Lachlan Eagling

Posted on • Originally published at lachlaneagling.com on

Python Lambda Functions - What The?

Like many a new, or seasoned Python developer I often find myself scratching my head at lambda functions whenever I encounter one, simply because of never taking the short amount of time to understand them.

After finally getting fed up with them being nothing more than a source of confusion and taking the time to learn about how they work, it turns out lambda functions are not that confusing after all.


So...What is a lambda function?

In Python (and many other languages) a lambda function is simply an anonymous function, containing only a single expression.

A simple example is a lambda function used to raise x to the power of 2.

lambda x: x ** 2

Here we have the lambda keyword to denote that this is a lambda function, followed by the lambda's argument x. The lambdas single expression is then on the right-hand side of the colon. The result of the expression will end up being the return value of the lambda function.

A more practical example is passing a lambda function as the filtering function passed into the built-in filter() function.

# Create a list of all even numbers between 0-10.
filtered = [n for n in filter(lambda x: x % 2 == 0, range(0,11))]
print(filtered)

>>> [0, 2, 4, 6, 8, 10]

In this example the lambda function is invoked by the filter function it is passed to. Each element in the iterable range(0,11) is then passed into the lambda's argument x, the lambdas expression evaluates if the number is odd or even and returns true or false accordingly. Only values that evaluated to true in the lambda function are then included in the resulting iterable created by the filter() function.

The same outcome can also be achieved with a regular Python function. This approach, despite being more verbose may be preferable as it is clearer exactly what the filter functions intended behaviour is, and if there is a requirement to write a unit test for the filter function.

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

# Create a list of all even numbers between 0-10.
filtered = [n for n in filter(filter_even, range(0,11))]
print(filtered)

Performance?

Given the vastly different syntax compared to a normal function definition, one would be forgiven for thinking that the Python runtime executes lambda functions differently, resulting in either some change in performance.

This is not the case, as can be seen in the below disassembled Python bytecode, the only difference is that the standard Python function has a name where the lambda function is simply named lambda.

print(dis("def filter_even(x): return x % 2 == 0"))

# Standard Python function bytecode
Disassembly of <code object filter_even at 0x101b0bea0, file "<dis>", line 1>:
  1 0 LOAD_FAST 0 (x)
              2 LOAD_CONST 1 (2)
              4 BINARY_MODULO
              6 LOAD_CONST 2 (0)
              8 COMPARE_OP 2 (==)
             10 RETURN_VALUE
print(dis("lambda x: x % 2 == 0"))

# Lambda function bytecode
Disassembly of <code object <lambda> at 0x10b59cd40, file "<dis>", line 1>:
  1 0 LOAD_FAST 0 (x)
              2 LOAD_CONST 1 (2)
              4 BINARY_MODULO
              6 LOAD_CONST 2 (0)
              8 COMPARE_OP 2 (==)
             10 RETURN_VALUE

Conclusion

The main benefit of lambda functions is the ability to define a single-use, single responsibility function that can be easily passed around.

There are however several drawbacks associated with them.

  • Unfamiliar syntax.
  • Not testable.
  • Not possible to provide a docstring.
  • Difficult to comprehend tracebacks due to lambda functions having no defined name.

Because the drawbacks largely outweigh the benefits, I would likely stay away from lambda functions for anything aside from simple functions provided as arguments to functions such as sorted(), map() and filter().

Discussion (4)

Collapse
zeljkobekcic profile image
TheRealZeljko

Not possible to provide a docstring

It is possible with the __doc__ variable. But if you want to do that you are IMO better of defining a function the standard way.


def test_stuff(a):
    """some doc string"""
    return a

print(test_stuff.__doc__)
# => 'some doc string'

test_other_stuff = lambda x: x
test_other_stuff.__doc__ = 'some other doc string'
print(test_other_stuff.__doc__)
# => 'some other doc string'
Collapse
lachlaneagling profile image
Lachlan Eagling Author

That is certainly one approach. Though as you highlighted it would definitely be better to just use a standard function if you had to provide a docstring.

Collapse
hanpari profile image
Pavel Morava • Edited on

There is nothing mysterious about lambda function and they have a larger usage than described in your conclusion.

For instance, simple currying:

f = lambda x: lambda y: x+y
increment = f(1)
increment(10)
11


Just kidding :)

Collapse
dmitrypolo profile image
dmitrypolo

Is there a reason you picked using list comprehension for consuming the generator as opposed to just wrapping the filter call in a list() method?