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?
The rule is that a lambda expression can only access local variables from an enclosing scope that are effectively final.
Because the local variables declared outside the lambda expression can be final or effectively final. The rule of final or effectively final is also applicable for method parameters and exception parameters. The this and super references inside a lambda expression body are the same as their enclosing scope.
Yes, sure. Normal name lookup rules apply.
Lambda expressions can use variables defined in an outer scope. We refer to these lambdas as capturing lambdas. They can capture static variables, instance variables, and local variables, but only local variables must be final or effectively final.
Inside lambda expression, we can't assign any value to some local variable declared outside the lambda expression. Because the local variables declared outside the lambda expression can be final or effectively final.
For example, a lambda expression can use an instance or static variable defined by its enclosing class. A lambda expression also has access to (both explicitly and implicitly), which refers to the invoking instance of the lambda expression’s enclosing class.
However, when a lambda expression uses a local variable from its enclosing scope, a special situation is created that is referred to as a variable capture. In this case, a lambda expression may only use local variables that are effectively final. An effectively final variable is one whose value does not change after it is first assigned.
An environment variable is a pair of strings that are stored in a function's version-specific configuration. The Lambda runtime makes environment variables available to your code and sets additional environment variables that contain information about the function and invocation request.
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With