[Python-ideas] Explicit variable capture list

Steven D'Aprano steve at pearwood.info
Tue Jan 19 18:51:15 EST 2016


On Tue, Jan 19, 2016 at 03:10:35PM +0100, haael at interia.pl wrote:
> 
> Hi
> 
> C++ has a nice feature of explicit variable capture list for lambdas:
> 
>     int a = 1, b = 2, c = 3;
>     auto fun = [a, b, c](int x, int y){ return a + b + c + x + y};

For the benefit of those who don't speak C++, could you explain what 
that does? Are C++ name binding semantics the same as Python's?

Specifically, inside fun, does "a" refer to the global a? If you rebind
global a, what happens to fun?

fun(0, 0)  # returns 6
a = 0
fun(0, 0)  # returns 5 or 6?



> This allows easy construction of closures. In Python to achieve that, you need to say:
> 
>     def make_closure(a, b, c):
>         def fun(x, y):
>             return a + b + c + x + y
>         return def
>     a = 1
>     b = 2
>     c = 3
>     fun = make_closure(a, b, c)

I cannot tell whether the C++ semantics above are the same as the Python 
semantics here. Andrew's response to you suggests that it is not.



> My proposal: create a special variable qualifier (like global and 
> nonlocal) to automatically capture variables

"Variables" is an ambiguous term. I don't want to get into a debate 
about "Python doesn't have variables", but it's not clear what you mean 
here. Python certainly has names, and values, and when you talk about 
"variables" do you mean the name or the value or both?


>     a = 1
>     b = 2
>     c = 3
>     def fun(x, y):
>         capture a, b, c
>         return a + b + c + x + y
> 
> This will have an effect that symbols a, b and c in the body of the 
> function have values as they had at the moment of function creation. 
> The variables a, b, c must be defined at the time of function 
> creation. If they are not, an error is thrown. 

If I have understood you correctly, we can already do that in 
Python, and don't even need a closure:

a, b, c = 1, 2, 3
fun = lambda x, y, a=a, b=b, c=c: a + b + c + x + y


will capture the current *value* of GLOBAL a, b and c, store them as 
default values, and use them as the LOCAL a, b and c.

You may consider it a strength or a weakness that they are exposed as 
regular function parameters:

fun(x, y)  # intended call signature
fun(x, y, a, b, c)  # actual call signature

but if you really care about hiding the extra parameters, a second 
approach will work:

from functools import partial
a, b, c = 1, 2, 3
fun = partial(lambda a, b, c, x, y: a + b + c + x + y, a, b, c)


If a, b, c are mutable objects, you can make a copy of the value:

fun = partial(lambda a, b, c, x, y: a + b + c + x + y, 
              a, b, copy.copy(c)
              )

for example.

Does your proposal behave any differently from these examples?


-- 
Steve


More information about the Python-ideas mailing list