lambda function acessing outside variable
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:
i = "original"
def print_i1():
print(i) # prints "changed" when called below
def print_i2(s=i): #default set at function creation, not call
print(s) # prints "original" when called below
i = "changed"
print_i1()
print_i2()
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.
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))