E731 do not assign a lambda expression, use a def

PythonLambdaFlake8Pep

Python Problem Overview


I get this pep8 warning whenever I use lambda expressions. Are lambda expressions not recommended? If not why?

Python Solutions


Solution 1 - Python

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 2x > > No: > > f = lambda x: 2x > > 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.

Solution 2 - Python

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.

Solution 3 - Python

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.

Solution 4 - Python

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.

Solution 5 - Python

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)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKechit GoyalView Question on Stackoverflow
Solution 1 - PythonGareth LattyView Answer on Stackoverflow
Solution 2 - PythoniankitView Answer on Stackoverflow
Solution 3 - PythonElmar PeiseView Answer on Stackoverflow
Solution 4 - PythonsimPView Answer on Stackoverflow
Solution 5 - PythonMario Cerón CharryView Answer on Stackoverflow