lambda function accessing outside variable

Question:

I wanted to play around with anonymous functions so I decided to make a simple prime finder. Here it is:

tests = []
end = int(1e2)
i = 3
while i <= end:
    a = map(lambda f:f(i),tests)
    if True not in a:
        tests.append(lambda x:x%i==0)
        print i
    print tests
    print "Test: "+str(i)
    print str(a)
    i+=2

What I find however, is that the i in the lambda x:x%i==0 is accessed each time, while i want it to be a literal number. how can I get it to become lambda x:x%3==0 instead?

Asked By: Hovestar

||

Answers:

Create a new function that returns the lambda. Then call that, passing in i as an argument. This will create a new binding scope.

def make_test (i):
   # this i refers to the parameter (which evaluates to the /value/ passed)
   return lambda x: x%i==0

# ..
# the /value/ resulting from evaluating the variable is passed
tests.append(make_test(i))
Answered By: user2864740

You can "capture" the i when creating the lambda

lambda x, i=i: x%i==0

This will set the i in the lambda’s context equal to whatever i was when it was created. you could also say lambda x, n=i: x%n==0 if you wanted. It’s not exactly capture, but it gets you what you need.


It’s an issue of lookup that’s analogous to the following with defined functions:

glob = "original"

def print_glob():
    print(glob) # prints "changed" when called below

def print_param(param=glob): # default set at function creation, not call
    print(param) # prints "original" when called below


glob = "changed"
print_glob()
print_param()
Answered By: Ryan Haining

The problem is that each of those functions in tests is referring to the variable i.

More commonly, you do this inside a function, in which case you have a local-to-the-defining-scope variable i, which gets stored in a closure, as nicely explained in These Nasty Closures.

But here, it’s even simpler: i is a global variable, so there is no closure. The functions are compiled to look up i as a global variable when run. Since i has changed, the functions will see the changed value when they run. Simple as that.


The traditional way around this (which works for both closures and globals) is fondly known as “the default-value hack”, even though it’s not really a hack. (See the explanation in the FAQ.) Ryan Haining’s answer explains how to do this:

lambda x, i=i: x%i==0

This creates a parameter named i, with a default value equal to the value of i at the time the function is created. Then, inside the function, when you access parameter i, and you get that value.


A different way around this, which may seem more familiar if you’re using to languages like JavaScript, is to create a function-creating function, and pass the value of i as an argument to that function-creating function, as in user2864740’s answer:

(lambda i: lambda x: x%i)(i)

This avoids “polluting” the signature of the function with an extra parameter (that someone could accidentally pass an argument to), but at the cost of creating and calling a function for no good reason.


A third way around this is to use partial. In cases where all you’re trying to do is partially apply a function, using partial instead of defining a wrapper function as a lambda can be cleaner.

Unfortunately, in this case, the function is hidden inside an operator, and the function operator.mod that exposes it doesn’t take keyword arguments, so you can’t usefully partial its second operand. So, this is a bad solution in this case. If you really wanted to, you could just write a wrapper that behaves better and partial that:

def opmod(a, b):
    return a % b

partial(operator.mod, b=i)

In this case, I think you’re better off with the other solutions; just keep this one in your head for cases where it is appropriate.

Answered By: abarnert
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.