18

I was using a lambda statement to perform math, and happened to repeatedly use one certain value. Therefore I was wondering if it was possible to assign and use a variable within a lambda statement.

I have tried things like:

a = lambda n:(b=3+2*n) #[my math here]

However this just raises errors, and I was wondering if there was a way to do this.

John Coleman
  • 51,337
  • 7
  • 54
  • 119
Professor_Joykill
  • 919
  • 3
  • 8
  • 20
  • 2
    https://stackoverflow.com/questions/6282042/assignment-inside-lambda-expression-in-python – Jason Stein Jul 26 '17 at 20:54
  • 1
    I don't think this really is a duplicate of that question. While the title is very similar, the actual problem seems to be a bit different. (Actually just wanted to vote for re-open, forgot about anti-dupe-hammer...) – tobias_k Jul 26 '17 at 21:01
  • You can assign variable in lambda using walrus operator (in Python 3.8). – SilentGuy Oct 23 '20 at 23:38

8 Answers8

25

Nope, you can't. Only expressions allowed in lambda:

lambda_expr        ::=  "lambda" [parameter_list]: expression
lambda_expr_nocond ::=  "lambda" [parameter_list]: expression_nocond

You could, however, define a second lambda inside the lambda and immediately call it with the parameter you want. (Whether that's really better might be another question.)

>>> a = lambda n: ((3+2*n), n*(3+2*n))  # for reference, with repetition
>>> a(42)
(87, 3654)
>>> a2 = lambda n: (lambda b: (b, n*b))(3+2*n)  # lambda inside lambda
>>> a2(42)
(87, 3654)
>>> a3 = lambda n: (lambda b=3+2*n: (b, n*b))()  # using default parameter
>>> a3(42)
(87, 3654)

Of course, both the outer and the inner lambda can have more than one parameter, i.e. you can define multiple "variables" at once. The benefit of this approach over, e.g., defining a second lambda outside of the first is, that you can still also use the original parameters (not possible if you invoked a with b pre-calculated) and you have to do the calculation for b only once (other than repeatedly invoking a function for the calculation of b within a).


Also, inspired by the top answer to the linked question, you could also define one or more variables as part of a list comprehension or generator within the lambda, and then get the next (first and only) result from that generator or list:

>>> a4 = lambda n: next((b, n*b) for b in [3+2*n])
>>> a4(42)
(87, 3654)

However, I think the intent behind the lambda-in-a-lambda is a bit clearer. Finally, keep in mind that instead of a one-line lambda, you could also just use a much clearer three-line def statement...


Also, starting with Python 3.8, there will be assignment expressions, which should make it possible to write something like this. (Tested with Python 3.8.10.)

>>> a5 = lambda n: ((b := 3+2*n), n*b)
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • 2
    To newer visitors: note that the last proposition does not work with Python 3.8 as assignment expressions are disallowed in lambda functions. – AdrienW Oct 20 '20 at 17:22
  • @AdrienW It doesn't work because he forgot a parenthesis: `a5 = (lambda n: ((b := 3+2*n), n*b))`. Other than that, assignment expressions work perfectly fine with lambdas. – Spartan Jun 29 '22 at 09:24
  • @AdrienW but you are right too, because of the parenthesis , it is not used directly with lambda, but within a tuple, or when parenthesizing as operator , with whatever object it evaluates too. – Spartan Jun 29 '22 at 10:01
  • @Spartan Thanks for pointing out, fixed the parens. (Not sure what you mean with your 2nd comment, though.) – tobias_k Jun 29 '22 at 10:27
  • @tobias_k try something like `lambda x: x:=1` and you will understand :) – Spartan Jun 30 '22 at 23:36
3

You can just pass your lambda an argument which passes it along to another argument if you wish:

>>> b = lambda x: 3 + 2*x
>>> a = lambda y: y * b(y)
>>> a(1)
5
>>> a(2)
14
Zoidberg
  • 425
  • 3
  • 10
  • No what I was trying to do is use the argument passed to the lambda to do calculations, but I repeatedly performed the operation `3+2*n` to the argument so I wanted to assign that to a variable. – Professor_Joykill Jul 26 '17 at 20:58
2

You can create 2 different lambda functions and pass one to the other. For example,

b = lambda x: 3+2*x
a = lambda y: [my math here using variable b(y)]
nanojohn
  • 572
  • 1
  • 3
  • 13
  • this is bad and considered as an anti pattern : https://docs.quantifiedcode.com/python-anti-patterns/correctness/assigning_a_lambda_to_a_variable.html – jossefaz Mar 15 '22 at 16:14
2

I've cooked up this recipe for python 3.8+ using PEP572 Assignment Expressions to assign arbitrary variables and execute arbitrary expressions.

# python 3.8b1
lambda a: (
    (bool(b:=a**2) or 1)
    and (bool(c:=a-b) or 1)
    and not print(f'compute: {a} + {b} + {c}')
    and (
        (ret:=a + b + c) or ret)
    )
)
tst(0) 
# prints: "compute: 0 + 0 + 0"; returns: 0
tst(1)
# prints: "compute: 1 + 1 + 0"; returns: 2
tst(8)
# prints: "compute: 8 + 64 + -56"; returns: 16

So the pattern is:

lambda: [*vars]: (
    (bool(a:=[expr(*vars)]) or 1)
    and (bool([expr]) or 1)
    and bool([always true expr])
    and not bool([always false expr])
    and (
        # parentheses required so `result:=` doesn't capture the `or result` part
        (result:=[result expr]) or result
    )
)

This may be simplified if you know the truthiness of any particular expression.

That being said, if you want to assign a variable to reuse inside a lambda, you probably should consider writing a normal function.

Terry Davis
  • 511
  • 5
  • 8
2

Im no expert at this, but the way i did it was by modifying globals() or locals() like this:

lambda: globals().__setitem__('some_variable', 'some value')

or if it's inside a function:

lambda: locals().__setitem__('some_variable', 'some value')

you could also use update() instead of __setitem__() if you wanted to, but that's a bit redundant.

Ryan Norooz
  • 1,358
  • 1
  • 12
  • 8
1

i hope that after 4 years my late answer will be useful

Python has setattr function to set attribute of given object. You can use it in your lambda expression.

setattr is useful when you want to programmatically-correctly (sorry) set some class or class' instance. It is not used frequently because it is easier to assign variables directly with = expression. But for lambdas... It is a salvation.

Also, iterables that support settign item (such as list), you can use <iter>.__setitem__.

Option 1. If you know the name of variable you're assigning.

x = lambda nameofvar, value: setattr(__builtins__, nameofvar, value)

# abc does not exist right now. Assigning and setting it to 10
x('abc', 10)
print(abc) # output: 10

alt, if you want to set object's attribute:

class MyClass:
    my_attr = False
    def __init__(self, value):
        self.value = value
myinstance = MyClass(25)

x = lambda obj, nameofvar, value: setattr(obj, nameofvar, value)
short_x = lambda nameofvar, value: setattr(MyClass, nameofvar, value)
# ^^^ this one works only for MyClass' attributes.

print(MyClass.my_attr) # output: False
x(MyClass, 'my_attr', True) # Changing MyClass' my_attr's value
print(MyClass.my_attr) # output: True
x(MyClass, 'my_attr2', 5) # Assigning new attribute to MyClass
print(MyClass.my_attr2) # output: 5
short_x('my_attr2', 123) # Setting MyClass' my_attr2 to 123
print(MyClass.my_attr2) # output: 123

print(myinstance.value) # output: 25
x(myinstance, 'value', 500)
print(myinstance.value) # output: 500

Option 2. Make a custom class and turn variable into its instance.

class Value:
    def __init__(self, value):
        self.value = value

x = lambda var, newvalue: setattr(var, 'value', newvalue)

a = Value(15)
print(a.value) # output: 15
x(a, 25)
print(a.value) # output: 25

Option 3. To set object's item.

lst = [15, 30, 45, 60, 75, 90]

x = lambda iterable, item, value: iterable.__setitem__(item, value)

print(lst) # output: [15, 30, 45, 60, 75, 90]
x(lst, 2, 1000)
print(lst) # output: [15, 30, 1000, 60, 75, 90]
NotLexa
  • 11
  • 1
0

You can instead use a bit of creativity, for example if you want to do some evaluation to an equation and assign the result to a variable it can be done like this:

class Results:
    res = 0

clas= Results()
setattr(clas, 'res', 3+2*4)
print(clas.res)
Ronald Saunfe
  • 561
  • 6
  • 20
0

You could rewrite this in a oop way, like this, maybe you are using an oop structure anyways so you could integrate it in there.

class example:
     def __init__(self):
         self.a = None

 object = example()
 a = lambda n:(setattr(object, "a", 3+2*n)) #[my math here]
 a(2)
 print(object.a)

Output:

7