Anonymous functions referencing local variables in python

How can I define anonymous functions in python, where bahaviour should depend on the value of a local variable at a particular point in time, and also take arguments

Example:

def callback(val1, val2): print "{0} {1}".format(val1, val2) i = 0 f0 = lambda x: callback(i, x) i = 1 f1 = lambda x: callback(i, x) f0(8) # prints "1, 8: but I'd like "0, 8" (value of 'i' when f0 was defined) f1(8) # prints "1, 8" 

Is it possible that it is possible without my callback in my class?

+7
source share
4 answers

Closing in python using functools.partial

 from functools import partial i = 0 f0 = partial(callback, i) i = 1 f1 = partial(callback, i) f0() # 0 f1() # 1 

partial is similar to lambda, but at this point it wraps the value in arg. Not appreciating him at his call.

Wrapping Only Some Arguments

Yes, partial will allow you to wrap any number of arguments, and the rest of the args and kwargs can then be passed to the resulting partial object so that it acts as if it were calling the original wrapped function ...

 def callback(val1, val2): print "{0} {1}".format(val1, val2) i = 0 x = 8 f0 = partial(callback, i) f0(x) # 0 8 

Essentially, you wrapped callback(val1, val2) in callback(val2) with val1 , which is already included as a close.

An example of a similar effect using lambda

If you really want to see how to do this with lambda closure, you can understand why it becomes ugly and partial, more preferable ...

 f0 = (lambda val1: lambda val2: callback(val1, val2))(i) 

You must transfer the scope variable to the scope of the outer function, and then reference that scope in the inner lambda function. SK.

Exception Tracking: partial vs lambda vs inested functions

With the influx of other answers, I thought that I would highlight another reason for using partial rather than lambda, or the closure of an internal / external function. Keep in mind that I mean closing a function. functools.partial corrects the trace you will receive when your wrapped function throws an exception ...

Consider this version, which will raise the division by zero:

 def callback(val1, val2): return val1 / val2 

Normal outside / inside closure

 def wrapper(fn, val1): def wrapped(val2): return fn(val1, val2) return wrapped f0 = wrapper(callback, i) f0(0) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in wrapped File "<stdin>", line 2, in callback ZeroDivisionError: integer division or modulo by zero 

lambda closure

 f0 = (lambda val1: lambda val2: callback(val1, val2))(i) f0(0) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <lambda> File "<stdin>", line 2, in callback ZeroDivisionError: integer division or modulo by zero 

And now for functools.partial

 f0 = partial(callback, i) f0(0) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in callback ZeroDivisionError: integer division or modulo by zero 
+8
source

You can achieve this by functools.partial:

 f0 = partial(callback, i) 
+1
source

You can create a function that creates a function. partial is another option, as mentioned by others.

 def repeater(s): def anon(): print s return anon greet = repeater("Hello, World") greet() 
0
source

Although using functools.partial is the best solution in this case, you can also create explicit closures:

 def callback_generator(val): def callback(): return val return callback i = 0 f0 = callback1(i) i = 1 f1 = callback1(i) 

This can also be done using lambdas, as shown in the comments below.

0
source

All Articles