I get this pep8 warning whenever I use lambda expressions. Are lambda expressions not recommended? If not why?
6 Answers
The recommendation in PEP-8 you are running into is:
Always use a def statement instead of an assignment statement that binds a lambda expression directly to a name.
Yes:
def f(x): return 2*x
No:
f = lambda x: 2*x
The first form means that the name of the resulting function object is specifically 'f' instead of the generic '<lambda>'. This is more useful for tracebacks and string representations in general. The use of the assignment statement eliminates the sole benefit a lambda expression can offer over an explicit def statement (i.e. that it can be embedded inside a larger expression)
Assigning lambdas to names basically just duplicates the functionality of def
- and in general, it's best to do something a single way to avoid confusion and increase clarity.
The legitimate use case for lambda is where you want to use a function without assigning it, e.g:
sorted(players, key=lambda player: player.rank)
In general, the main argument against doing this is that def
statements will result in more lines of code. My main response to that would be: yes, and that is fine. Unless you are code golfing, minimising the number of lines isn't something you should be doing: go for clear over short.

- 86,389
- 17
- 178
- 183
-
2@g33kz0r Prettier is fine in a codebase that's entirely your own (althought it's hard to guarentee that, things often migrate), but elsewhere, it's best to try and follow standards to help others read your code more effectively. Lambda holds a very specific purpose in Python and using it in this way just makes your code less readable, and objectively worse (as the answer states, you get a proper name on the function object, which is much better for debugging). – Gareth Latty Feb 16 '15 at 23:59
-
not wanting to start a flame war, but statements like "using it in this way just makes your code less readable" seem highly subjective to me. – Feb 17 '15 at 15:24
-
2@g33kz0r At the very least, function definitions are a much more obvious and commonly used language feature than lambda, making them easier to understand. If you really think it's better in terms of readability, it's *still* worse in terms of functionality and for debugging. – Gareth Latty Feb 17 '15 at 15:58
-
19I don't see how it's worse. The traceback is still going to include the errant line number and source file. One might say "f" whereas the other says "lambda". Maybe the lambda error is easier to scan because it's not a single-character function name, or a poorly-named long name? – Feb 17 '15 at 19:08
-
6@g33kz0r Well, sure, if you assume the rest of your code is going to have poor quality, following conventions won't gain you much. In general, no, it's not the end of the world, but it's still a bad idea. – Gareth Latty Feb 17 '15 at 20:42
-
69This answer is not very helpful, because when running the suggested approach of using `def` through the PEP8 checker, you get `E704 multiple statements on one line (def)`, and if you split it into two lines you get `E301 expected 1 blank line, found 0` :-/ – Adam Spiers Feb 20 '15 at 14:10
-
3@AdamSpiers Putting a block on the same line is generally considered bad form - I suspect it's given like that in the PEP for similarity, but you should really split it onto two lines. Not sure what your second problem is, but it is not from that. – Gareth Latty Feb 20 '15 at 15:07
-
5I agree it should be split. My points were that a) it is not split in the answer's code above, causing E704, and b) if you split it, you need an ugly blank line above it to avoid E301. – Adam Spiers Feb 21 '15 at 02:10
-
5I use lambdas when I want to emphasize a pure function (no side effects), and sometimes I have to use the same function in two places, i.e. groupby and sort together. So I ignore this convention. – manu Jan 28 '16 at 21:53
-
@manu Agreed. I also assign zero argument lambdas for deferred evaluation. Example: I have a bunch of switch cases (implemented as a dictionary of lambdas), about half of them use the maximum radius; DRY means I don't want to copy-paste the definition of max radius in each case, on the other hand, I don't want to compute it pointlessly when I run cases that don't need it. So I define max radius as a zero-arg lambda. – JBGreen Mar 08 '16 at 15:21
-
2This is a good description of the PEP-8 warning, and thus it answers the question, but I always disable E731, and in fact I recommend that others do so as well. Using def for short functions is pretty ugly. I was tasked with standardizing the application I was involved in to adhere to PEP-8 rules, but for this specific rule everybody on the team thought it was a significant change for the worse. Don't take my word for it -- write 10 different short lambda functions on 10 lines, change them to defs (one line or multiple lines), and decide for yourself. But I think the answer is clear. – TheGerm Jun 01 '16 at 18:00
-
3@TheGerm: I really don't understand that thinking - I don't think it's ugly at all, and using def is consistent (with longer functions, and if the function itself later becomes long), and has benefits besides (the function name for debugging). In any case, the whole point of having a standard is to provide consistency - sticking to it may not be perfect for your eye, but it will mean everyone can get on with it. Everyone is, of course, free to do what they want in their code, but I'd always recommend sticking to the standard. – Gareth Latty Jun 01 '16 at 18:03
-
3@GarethLatty, I am normally a big adherent to "sticking to the standard". This was just a case, though, where I (and others on my team) thought it was a direct detriment to readability. Our use case, if you care to know, was multiple short formatting functions (e.g. add percent sign, showing last name then first name) that could be applied to various grid columns. – TheGerm Jun 01 '16 at 18:13
-
2In my mind, if it's something you **call**, use `def g: ... g()`. If it's something you **pass as an argument**, use `f = lambda ... h(f)`. – Sergey Orshanskiy Apr 24 '17 at 18:45
-
2Note that if you're creating these functions in a loop, using `def` will overwrite the last definition of the function in the loop, while `lambda` will create a new function each time. If you're using a loop to make a list of functions, you'll want to use lambdas, otherwise you'll only have one function under the hood. – BallpointBen Apr 17 '18 at 22:45
-
4@BallpointBen I'm not quite sure what you mean, but as far as I know (and just tried to test), that isn't true. Can you give an example of this? – Gareth Latty Apr 18 '18 at 21:13
-
1for sorting key in given example i prefer using [`operator.attrgetter`](https://docs.python.org/3/library/operator.html#operator.attrgetter) to `lambda` – Azat Ibrakov Apr 23 '18 at 16:50
-
@AzatIbrakov Agreed, it's a good option alongside it's siblings `itemgetter` and `methodcaller` for relatively simple functions. – Gareth Latty Apr 24 '18 at 12:05
-
Imagine you need to accept a key function as a parameter to another function. You might want the default to be something simple like `lambda x: True` in case the outer parameter is `None`. You won't be able to write the lambda in-line later when you call for filter or sort or something, so you need to make it a lambda. To define it with `def` would add much more code that is inside an `if` check for `is None`. But for a `lambda` this can be done in one line and is much, much cleaner and simpler. – ely Oct 15 '20 at 00:40
-
2Kinda surprised no one has mentioned the #1 reason why you might want to assign a lambda to a variable - because python's type inference can pick up the right type for that automatically whereas for `def`s it cannot. That means for very simple expressions that you just want to break up and label, you not only have to use 2 lines for def, but you *also* have to properly type annotate which would be redundant over what inference can already figure out and can generate lots of fun bugs down the line when the type of the thing changes but you don't nkow to update the annotation god knows where – George Mauer Mar 09 '22 at 23:02
-
I'm getting the E731 because I allow the API caller to pass a function, a string, or None. So, I have – Ion Freeman May 11 '22 at 22:03
-
@BallpointBen That's demonstrably not true, though it can be hard to prove to yourself. Most of the time, when we define functions in a loop, we define them all using a variable from the outer context — in which case, they'll all be defined the SAME because they're all referencing the same variable that only has one value at any given time. But it is possible to `def` functions in a loop that have different bodies. Here's one way: `fxns = []; for i in range(5): def f(x, n=f"{i}"): return x + int(n); fxns.append(f)` If you subsequently run `[f(5) for f in fxns]`, you'll get `[5, 6, 7, 8, 9]`. – FeRD Mar 15 '23 at 18:29
-
(And lambdas have the same problem, BTW. If we tried to use `for i in range(5): f = lambda x: x + i; fxns.append(f)` we'd find that all of our lambdas produce the same value, using whatever the current value of `i` is.) – FeRD Mar 15 '23 at 18:32
Here is the story, I had a simple lambda function which I was using twice.
a = map(lambda x : x + offset, simple_list)
b = map(lambda x : x + offset, another_simple_list)
This is just for the representation, I have faced couple of different versions of this.
Now, to keep things DRY, I start to reuse this common lambda.
f = lambda x : x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
At this point my code quality checker complains about lambda being a named function so I convert it into a function.
def f(x):
return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Now the checker complains that a function has to be bounded by one blank line before and after.
def f(x):
return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Here we have now 6 lines of code instead of original 2 lines with no increase in readability and no increase in being pythonic. At this point the code checker complains about the function not having docstrings.
In my opinion this rule better be avoided and broken when it makes sense, use your judgement.

- 47,830
- 31
- 106
- 135

- 8,806
- 10
- 50
- 56
-
21`a = [x + offset for x in simple_list]`. No need to use `map` and `lambda` here. – Georgy Apr 23 '18 at 16:39
-
22@Georgy I believe the point was to move the `x + offset` portion to an abstracted location that can be updated without changing more than one line of code. With list comprehensions as you mentioned, you would still need two lines of code that contained `x + offset` they would just now be in list comprehensions. In order to pull those out as the author wanted, you would need a `def` or `lambda`. – Julian Nov 03 '18 at 02:18
-
1@Julian Apart from `def` and `lambda` one could also use [functools.partial](https://docs.python.org/3/library/functools.html#functools.partial): `f = partial(operator.add, offset)` and then `a = list(map(f, simple_list))`. – Georgy Nov 18 '18 at 18:33
-
1What about `def f(x): return x + offset` (i.e., a simple function defined on a single line)? At least with flake8 I do not get complaints about blank lines. – DocOc Aug 22 '19 at 10:50
-
1@Julian In some cases you can use a nested comprehension: `a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]` – wjandrea Dec 29 '19 at 21:03
-
Also, if this is a function you only use in your own code, just prefix it with an underscore (`_foo`) and it will not need to have a docstring. – Dominik George Mar 06 '20 at 22:01
-
1hold on! what sort of 2010 nonsense is this? You think you're done? Of course you are not. *Now* you have to add type annotations and maintain those types. And depending on your code enforcement policies you might need to add a doc comment as well. – George Mauer Mar 09 '22 at 23:06
Lattyware is absolutely right: Basically PEP-8 wants you to avoid things like
f = lambda x: 2 * x
and instead use
def f(x):
return 2 * x
However, as addressed in a recent bugreport (Aug 2014), statements such as the following are now compliant:
a.f = lambda x: 2 * x
a["f"] = lambda x: 2 * x
Since my PEP-8 checker doesn't implement this correctly yet, I turned off E731 for the time being.

- 14,014
- 3
- 21
- 40
-
12Even when using `def`, the PEP8 checker complains with `E301 expected 1 blank line, found 0`, so you then have to add an ugly blank line before it. – Adam Spiers Feb 20 '15 at 14:11
-
I don't think it's just against having assignments in lambda. I have no assignments in mine, just True or False return, yet it gives me C3001 (unnecessary-lambda-assignment) `has_attribute = lambda x: x["attribute_name"] != 1 and x["attribute_name"] is not None` . Looks like a pretty arbitrary stylistic decision. – SwissNavy Jan 27 '23 at 11:39
I also encountered a situation in which it was even impossible to use a def(ined) function.
class SomeClass(object):
# pep-8 does not allow this
f = lambda x: x + 1 # NOQA
def not_reachable(self, x):
return x + 1
@staticmethod
def also_not_reachable(x):
return x + 1
@classmethod
def also_not_reachable(cls, x):
return x + 1
some_mapping = {
'object1': {'name': "Object 1", 'func': f},
'object2': {'name': "Object 2", 'func': some_other_func},
}
In this case, I really wanted to make a mapping which belonged to the class. Some objects in the mapping needed the same function. It would be illogical to put the a named function outside of the class. I have not found a way to refer to a method (staticmethod, classmethod or normal) from inside the class body. SomeClass does not exist yet when the code is run. So referring to it from the class isn't possible either.

- 371
- 2
- 9
-
1You could refer to `also_not_reachable` in the mapping definition as `SomeClass.also_not_reachable` – Jan Matějka Sep 26 '18 at 23:07
-
5I don't know what point you're trying to make here. Every one of your function names is as reachable as `f` in both 2.7 and 3.5 for me – Eric Nov 12 '18 at 03:15
-
Nope, all the functions, except for the lambda function, are not reachable from within the Class body. You'll get a AttributeError: type object 'SomeClass' has no attribute '...' if you try to access one of those function in the some_mapping object. – simP Jun 26 '19 at 09:44
-
3@simP all of them are perfectly accessible. The ones with `@staticmethod` and `@classmethod` don't need an object, just `SomeClass.also_not_reachable` (although they need distinctive names). If you need to access them from class methods just use `self.also_not_reachable` – ababak Sep 16 '19 at 11:34
-
2@simP maybe you should rename your `*not_reachable` methods as `not_as_easily_reachable_from_class_definition_as_a_lambda` xD – Romain Vincent Mar 19 '20 at 14:27
-
@RomainVincent https://stackoverflow.com/questions/41921255/staticmethod-object-is-not-callable – simP Mar 24 '20 at 08:54
-
-
All of ``f``, ``not_reachable`` and the last defined ``also_not_reachable`` are available in the class body under their given name. For example, ``'object2': {'name': "Object 2", 'func': not_reachable}`` works flawlessly in both Python2 and Python3. Since the functions are unbound in the class body, defining it as a regular function (no ``self``, no ``staticmethod``) is correct, by the way. – MisterMiyagi Aug 03 '21 at 11:17
Further to Gareth Latty's answer, another reason to prefer defs is type safety.
The following code will pass mypy type checks despite the fact it contains a type error:
y = lambda x: x**2
print(y("fred"))
We can make it type-safe using the annotated code below, and now mypy will detect an error as expected.
from typing import Callable
y: Callable[[int], int] = lambda x: x**2
print(y("fred"))
However, this looks a bit unwieldy. Let's compare with the type-safe def
alternative below.
def y(x: int) -> int:
return x**2
print(y("fred"))
Arguable, the the def
version is more readable and concise (objectively, although it takes two lines, it has fewer overall characters, and does not require an additional import).

- 31
- 6
This works for me in a class, remove lambda expression and use def instead, changing this...
def set_every(self, every: int = 1, time_unit: int = TimeUnit.Day):
every_func = lambda x: "*" if x == 1 else "*/" + str(x)
if TimeUnit.has_value(time_unit):
self.month_of_year = "*"
self.day_of_month = "*" if time_unit != TimeUnit.Day else every_func(every)
self.day_of_week = "*" if time_unit != TimeUnit.Week else every_func(every)
by this...
def set_every(self, every: int = 1, time_unit: int = TimeUnit.Day):
def every_func(x: int) -> str: return "*" if x == 1 else "*/" + str(x)
if TimeUnit.has_value(time_unit):
self.month_of_year = "*"
self.day_of_month = "*" if time_unit != TimeUnit.Day else every_func(every)
self.day_of_week = "*" if time_unit != TimeUnit.Week else every_func(every)

- 29
- 3
-
Please add further details to expand on your answer, such as working code or documentation citations. – Community Aug 29 '21 at 11:18
-
The OP never said his code doesn't work. It's only a warning, since it is a non-standard coding practice – Ankit Khettry Nov 01 '21 at 15:32