The world’s leading publication for data science, AI, and ML professionals.

Should You Define Named Python Functions Using lambda?

Doing so is against PEP8, so why so many authors suggest this?

PYTHON PROGRAMMING

Many authors claim that in order to shorten your code, you can use lambda functions to define functions and assign them to a name. Some beginners may feel like trying this approach; to be honest, not only did I try it at some point, but also I enjoyed it too much.

Note that this is a different situation from using an unnamed lambda function as an argument to a higher-order function, like here:

>>> x = [
...     ("Johny Faint", 197),
...     ("Jon Krohn", 187),
...     ("Anna Vanna", 178),
...     ("Michael Ginger", 165),
...     ("Viola Farola", 189)
... ]
>>> sorted(x, key=lambda x: -x[1])
[('Johny Faint', 197), ('Viola Farola', 189), ('Jon Krohn', 187), ('Anna Vanna', 178), ('Michael Ginger', 165)]

We will talk about assigning a lambda function to a name, like here:

>>> sort_fun = lambda x: -x[1]

The difference lies in the assignment, represented by =, which here means that we define function sort_fun(x), which will formally be a function with one argument, x. The function assumes x is an iterable with at least two elements. Of course, we know that this very function was constructed to be used for the same sorting as the one used above, like here:

>>> sorted(x, key=sort_fun)
[('Johny Faint', 197), ('Viola Farola', 189), ('Jon Krohn', 187), ('Anna Vanna', 178), ('Michael Ginger', 165)]

These two approaches differ. When we define a lambda function inside a higher-order function, it cannot be used elsewhere; and it is strictly dedicated to this particular use. When we define a lambda function and assign it to a name (sort_fun), it can be used elsewhere in the code. Also, in Python, functions are functions irrespective of how they were defined, whether using def or lambda; hence, sort_fun() is a valid Python function.

This article deals with named lambda functions; that is, lambda functions that are defined and assigned to a name. We will discuss whether you should define functions that way, or rather you should avoid doing that. We will see what PEP8 says about this, but also we will perform our own comparison of def-defined and lambda-defined functions. This will help us decide whether lambda indeed offers an alternative way of defining simple functions in Python.

What does PEP8 say about this?

Although over 20-year old, we still use PEP8 as the "Style Guide for Python Code." So, if you need to check something related to coding style, PEP8 should be your first source.

The idea of using assigned lambda functions goes against PEP8’s recommendation, which uses precise wording to state that you should not do this:

Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier…

So, does this really mean that using a named lambda function is indeed against PEP8? I see two possibilities:

  1. No, it does not! This lambda-based PEP8 recommendation is outdated. For example, many developers disagree with the maximum line length of 79 characters that PEP8 recommends, considering it the breath of the old. Along the same lines, the lambda suggestion is outdated, and we should not worry about going against PEP8. Wasn’t PEP8 created over twenty years ago, back in 2001? Should we indeed follow so old a style guide in something as modern as the Python programming language?
  2. Yes, it does! The authors who suggest to break this lambda-based recommendation rely on only one premise: the number of characters. This would mean that if something is shorter, it’s better. The world is not that simple, and programming is not different: the brevity of lambda functions is an insufficient factor alone. Listen to PEP8 and don’t use named lambda functions!

To be honest, some time ago I myself occasionally used such named lambda definitions. I simply liked the way they looked: concise and different. Now I think it was this difference what attracted me in this approach. I know I tend to like complex solutions, ones that look different and atypical, as they make me feel I am a good programmer. But this is so misleading! Writing complex code does not mean being a good programmer. Now that I know this, I try to be careful and double-check whether or not I am overdoing with unnecessary complexity.

So, let’s be objective and analyze whether named lambda-based function definitions can be better – and if so, when – than the def-based function definitions.

Be aware that we do not discuss using lambdas in general, but only named lambda definitions, which assign a lambda Function to a name, like here:

foo = lambda x, u: f"{x} and {u}"

Whatever we will come up with has nothing to do with using lambdas in higher-order functions.

Defining functions using lambda and def

Let’s make this comparison practical. To this end, I will use four functions:

  • a function without arguments
  • a function with one argument
  • a function with two arguments
  • a function with three arguments

For the last one, we will use a default value of one of the arguments. Here are the functions, defined in a traditional way:

def weather():
    return "beautiful weather"

def square(x):
    return x**2

def multiply(x, m):
    """Multiply x by m.

    >>> multiply(10, 5)
    50
    >>> multiply("a", 3)
    'aaa'
    """
    return x * m

def join_sep(iter1, iter2, sep="-"):
    return [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

Let’s see what these functions look like when defined using lambdas:

weather = lambda: "beautiful weather"

square = lambda x: x**2

multiply = lambda x, m: x * m

join_sep =lambda iter1, iter2, sep="-": [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

We will compare the two approaches in terms of several aspects.

Code brevity and visual clutter

The lambda code definitely looks more dense. Is it a good thing? Notice that assigned lambda functions are not much shorter: they replace the following characters: def, ():, and return with = lambda. So, instead of 12 characters, they use 7, so only 5 characters fewe r(ignoring white spaces). That’s not a lot.

I think the feeling of lambda definitions’ greater density comes from presenting them in one line. Nevertheless, we can do the same using def:

def square(x): return x**2

This definition and the lambda one do not differ much in terms of length, do they? However, using the def definition for such short functions, we use a new line after the first line for a reason – for me, it’s not more characters; instead, it helps avoid the visual clutter.

So, in terms of code brevity, I do not really see the point in choosing lambda definitions over def definitions.

In terms of visual clutter, lambda definitions are in my eyes worse, meaning more cluttered. Compare, again, the multiply() function defined using lambda:

multiply = lambda x, m: x * m

and def:

def multiply(x, m):
    return x * m

The additional white space in the latter helps one to visually group the function into two elements:

  1. the function’s signature: def multiply(x, m):
  2. the function’s body: return x * m

In lambda definitions, there is no such visual differentiation. Instead, we get visual clutter and density, which call for additional mental work needed to distinguish the function’s signature and body. What’s worse, we do not directly see the signature! We must create it in our minds.

There’s one more visual aspect of lambda definitions that I don’t like, which also makes them visually cluttered. Namely, the function’s name and its arguments do not go together but are separated with "= lambda ".

Clarity, no annotations (type hints)

So, despite of what some authors say, lambda definition’s brevity comes with visual clutter and increased code density. Hence, for me this brevity is a disadvantage rather than an advantage.

Maybe the lambda functions’ code is clearer? In the code block below, is the latter definition clearer than the former?

def square(x):
    return x**2

square = lambda x: x**2

While I do not have problems with reading and understanding this lambda function, I wouldn’t say it’s clearer than the one defined using the def keyword. To be honest, since I am accustomed to def function definitions, the former is even clearer to me.

Our above discussion related to visual clutter and code density applies here, too. I like the two-line def definition, which immediately shows the function’s signature and body. The lambda definition, on the opposite, requires me to read through the whole code in one line, in order to see the function’s signature and body. This decreases code readability – even for this simple function.

Let’s see what it looks like in a function with two arguments:

def multiply(x, m):
    return x * m

multiply = lambda x, m: x * m

This time, the def version seems significantly clearer to me. This is, again, due to some problems with visually distinguishing the function’s signature (including function arguments) and body.

It’s not without reason that whoever suggests lambda definitions, does that for short and simple functions. We can see this in the definitions of the join_sep function:

def join_sep(iter1, iter2, sep="-"):
    return [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

join_sep =lambda iter1, iter2, sep="-": [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

Is there anyone to choose join_sep() defined using the lambda keyword as the better one? For me, only the former can be considered readable while the latter is overly difficult – even though I do understand this lambda definition. In this case, understanding does not mean appreciation.

I provided this example only to reinforce the point that even if you do decide to use lambda definitions, you should do it for simple and short functions; join_sep() is not simple enough.

The def definitions, however, enable us to do one more thing in order to increase its clarity: docstrings. Let me repeat the example of multiply()‘s definition:

def multiply(x, m):
    """Multiply x by m.

    >>> multiply(10, 5)
    50
    >>> multiply("a", 3)
    'aaa'
    """
    return x * m

Docstrings are a powerful way of documenting functions and enriching them with unit tests (i.e., doctests). You add any type of documentation to named lambda functions, except for inline comments. On the other hand, I suppose you would not use lambda for a function that requires a docstring. I would, however, say that multiply() does not require a docstring – but it is clearer with it.

Clarity, with annotations (type hints)

We know that when used wisely, type hints can increase function clarity. Consider these two examples:

from numbers import Number

def square(x: Number) -> Number:
    return x**2

square = lambda x: x**2
def multiply(x: Number, m: Number) -> Number:
    return x * m

multiply = lambda x, m: x * m

We cannot use function annotations in lambda definitions. So, whenever you have a function whose signature uses helpful type hints, a def-based definition will always be better, as it will convey more information about the function than its lambda definition.

Now, a short mental exercise: imagine multiply() with both docstring and type hints, and see how much better this definition would be from the bare named lambda definition.

The above examples suggest that functions with two arguments (or more, for that matter) may lack type hints even more than single-argument functions.

Conclusion

We compared lambda-based and def-based function definitions in Python. In doing so, we used only two aspects: brevity and visual clutter; and clarity. The comparison was not very rich – because we did not need to make it rich. From whichever angle we looked at the two types of definition, def always proved better than named lambda.

Even in the case of the simplest functions, I did not find sufficient arguments for defining them using lambda. More often than not, the corresponding def definition was clearer, and even if a little longer, its code was less visually cluttered and dense.

It’s difficult for me to imagine a two-argument function, especially with a default value of at least one of the arguments, that would be clearer when lambda defined than when def defined.

Finally, def definitions enables the developer to use three powerful tools that are not available in lambda functions:

  • function annotation (type hints)
  • docstrings
  • unit tests, via doctests inside the doctest

Thanks to them, we can make def functions much more informative.


Thanks for reading this article. I hope you enjoyed it.

And I do hope I have convinced you. Even if both types of function definition seem equally fine to you— even then I would not use named lambda definitions. This is because using them, you do not gain anything, at the same time risking that others will disagree with you. And if even that does not convince you, remember that doing so you’re going against PEP8.

So, why would you want to use named lambda definitions when assigning a function to a name?

Resources

PEP 8 – Style Guide for Python Code

Python’s Type Hinting: Friend, Foe, or Just a Headache?

Lambda Functions with Practical Examples in Python

doctest – Test interactive Python examples – Python 3.11.0 documentation


Join Medium with my referral link – Marcin Kozak


Related Articles