Search code examples
pythonlambdaclosures

is it possible to have nonlocal in python lambda function?


right now my lambda function is :

In [40]: f=lambda x, count=0: count if not(x%3==0) else count+1

In [41]: [f(x) for x in range(10)]
Out[41]: [1, 0, 0, 1, 0, 0, 1, 0, 0, 1]

But what I really want is to accumulate the count, so that the output would be:

Out[42]: [1, 1, 1, 2, 2, 2, 3, 3, 3, 4]

I am looking for some code like:

f=lambda x, count=0: nonlocal count if not(x%3==0) else count+1

but got a syntax error message for the use of "nonlocal". So my question is: is there a way to define nonlocal variable in lambda?


Solution

  • In general, lambda functions should be pure functions, that is why they are purposefully restricted to expressions.

    As such, you cannot assign to a nonlocal/global variable in a lambda expression (although, you can use assignment expressions to assign to local variables).

    If you want to use nonlocal/global and assign to a variable, you can always just use a function definition statement. You never have to use a lambda expression to create a function. And lambda expressions are purposefully restricted in their capabilities. If you want something beyond that, just use a function definition statement.

    But in this case, you shouldn't do that In this case, you are trying to use a list-comprehension for side-effects. This is generally confusing and considered an anti-pattern. It is better to simply use a for-loop:

    result = []
    count = 0
    for i in range(10):
        if i%3 == 0:
            count += 1
        result.append(count)
    

    This is perfectly pythonic.

    But if you wanted a more functional programming approach, you should use itertools.accumulate:

    import itertools
    result = itertools.accumulate(map(lambda x: int(x%3 == 0), range(10)))
    

    Lambda expression cannot contain statements like nonlocal or global. But you should choose the right tool for the job, and in this case, it is either just a normal for-loop, or itertools.accumualte.