36

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?

SuibianP
  • 99
  • 1
  • 11
Hovestar
  • 1,544
  • 4
  • 18
  • 26
  • 1
    Also see [These Nasty Closures](http://code.activestate.com/recipes/502271/) for a better explanation than the one in the official docs. But the short version is that each of these test functions is effectively a closure around the same variable `i`, and `i` keeps changing value. (This isn't quite accurate, because global variables don't actually need to be stored in closures, but the effect is the same.) – abarnert Jan 10 '14 at 20:54

3 Answers3

77

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()
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • 3
    This works because default arguments are evaluated when the function is *created*, whereas variable look up is done when the function is *called*. – Steven Rumbalski Jan 10 '14 at 20:52
10

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.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • I could not find anything related to that on the link you provided. – m3nda Mar 12 '18 at 00:49
  • @erm3nda There are at least two links in the answer, and I have no idea what "that" refers to in your comment. – abarnert Mar 12 '18 at 00:50
  • 1
    Sorry. About the default value hack at http://docs.python.org/3/faq/design.html#why-are-default-values-shared-between-objects – m3nda Mar 12 '18 at 00:51
  • @erm3nda Ah, that seems to be in the Programming chapter of the FAQ, not the Design chapter. I don't know if that changed over the last 4 years, or if you were the first one to actually follow the link in all that time, but either way, thanks for catching it, and I'll edit. – abarnert Mar 12 '18 at 00:54
  • https://docs.python.org/3/faq/programming.html#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result thanks. – m3nda Mar 12 '18 at 01:05
  • @erm3nda By the way, something that helps me for understanding, which both links make a bit more confusing rather than helping, is that the difference between Python vs. say, many Lisps has nothing to do with closures, but with looping. In both languages, `i` closes over the local variable. But in Python, we're updating that local variable after the closure is being created, while in most Lisps, we're either recursing, or using a macro that creates a new variable each time through the loop, not `setq`ing the variable; either way, the variable we capture never changes, so things just work. – abarnert Mar 12 '18 at 01:19
2

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))
user2864740
  • 60,010
  • 15
  • 145
  • 220