I get this pep8 warning whenever I use lambda expressions. Are lambda expressions not recommended? If not why?
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.
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
:-/ –
Edraedrea def g: ... g()
. If it's something you pass as an argument, use f = lambda ... h(f)
. –
Ible 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. –
Lovesome operator.attrgetter
to lambda
–
Tanika itemgetter
and methodcaller
for relatively simple functions. –
Oared 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. –
Tree 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 –
Rivas 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]
. –
Elanorelapid 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.) –
Elanorelapid 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.
a = [x + offset for x in simple_list]
. No need to use map
and lambda
here. –
Strother 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
. –
Polynesian def
and lambda
one could also use functools.partial: f = partial(operator.add, offset)
and then a = list(map(f, simple_list))
. –
Strother 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. –
Crista a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
–
Town _foo
) and it will not need to have a docstring. –
Guidon 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.
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. –
Edraedrea has_attribute = lambda x: x["attribute_name"] != 1 and x["attribute_name"] is not None
. Looks like a pretty arbitrary stylistic decision. –
Degroot 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.
also_not_reachable
in the mapping definition as SomeClass.also_not_reachable
–
Canicula f
in both 2.7 and 3.5 for me –
Sergiosergipe @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
–
Faunia *not_reachable
methods as not_as_easily_reachable_from_class_definition_as_a_lambda
xD –
Millicentmillie 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. –
Antisocial The main reason seems to be that using lambda expressions to create new functions decreases code readability over using the def keyword to create those functions. If you have some code, and some functions are created with def and others with lambda, it can get confusing to tell which are functions and which are variables.
In short, the def keyword is meant to be used for creating new functions, and lambda is meant for anonymous functions (i.e. functions not tied to a name). Defining a variable as the result of some lambda expression seems to fly in the face of the intended use case for a lambda expression.
I recently encountered very legitimate reason to assign lambda
expression directly (as always it CAN be avoided by creating extra code). Basically I needed to read the value of a variable at a later date, but I also need to construct something from it that the interface I use expects to be lazy-evaluated.
#my_module.py
from ... import get_global_config, Other
class Some(Other):
SOME_MAPPING = { ... }
def __init__(self, ...):
some_attr = lambda: SOME_MAPPING[
get_global_config().some_param
]
...
super().__init__(self, some_attr, ...)
When I construct the object the global_config
in question may or may not be set/constructed/fully configured, so I need to defer getting the value of the configuration to a moment when it's actually needed.
Under normal circumstances this could be avoided by using properties, however the interface I'm using/inheriting requires me to assign something concrete or a closure of type Callable[[], ...]
to a particular attribute, and I didn't really feel like refactoring 1000s of lines of code at the moment.
If you are using mypy then 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).
def
by stating it provides "type safety", when type safety is not a feature in the language? Type hinting and safety are not the same thing, and there is a danger in implicitly thinking that hinting provides safety. –
Wash 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)
© 2022 - 2024 — McMap. All rights reserved.
flake8
(flake8.pycqa.org) – Notification