[Tutor] python closures
Kent Johnson
kent37 at tds.net
Mon Nov 30 16:16:05 CET 2009
On Mon, Nov 30, 2009 at 5:24 AM, spir <denis.spir at free.fr> wrote:
> Hello,
>
> Below startup definitions:
>
> x = 1
>
> def f():
> n = 1
> def g0(a):
> print (x + n + a)
> return g0
>
>
> I'm surprised the snippet below works as expected (py 2.6) without any trick:
>
> g = f()
> g(1) # --> 3
>
> This means a (real) closure is built for g0, or what?
Yes, Python has had real (read-only) closures since 2.1 when nested
scopes where introduced:
http://docs.python.org/dev/whatsnew/2.1.html#pep-227-nested-scopes
Python 3 introduces the 'nonlocal' keyword which allows assignment to
names in enclosing scopes, presumably ending at last the debate about
whether Python has 'real' closures:
http://www.python.org/dev/peps/pep-3104/
> Thought I would need instead to use the old trick of pseudo default-parameters:
>
> def f():
> n = 1
> def g0(a, n=n, x=x):
> print (x + n + a)
> return g0
>
> to let the inner func g0 "remember" outer values. Why is this idiom used, then? Has something changed, or do I miss a relevant point?
That has not been needed since 2.1 though it is still useful when
closures are created in a loop (because closures are kind of late
bound - I'm not sure the exact technical explanation):
In [13]: def f():
....: l = []
....: for i in range(3):
....: def g():
....: print i
....: l.append(g)
....: return l
In [14]: for g in f(): g()
....:
2
2
2
But with the default argument it captures the value of i each time
through the loop:
In [15]: def f():
....: l = []
....: for i in range(3):
....: def g(i=i):
....: print i
....: l.append(g)
....: return l
In [16]: for g in f(): g()
....:
0
1
2
> The bit below also works:
>
> x = 2
> ...
> g(1) # --> 4
>
> which seems to indicate python really embeds "symbolic references" (*) to outer *variables*, when creating a closure for g0. Not "pointer references" (**), otherwise the replacement of x would not be seen by the closure --like in the case of default-parameter.
In your first definition of f(), x is global and not included in the
closure. This is the same behaviour you would have in older versions.
In your second definition of f(), x is bound to a default argument and
changing the global x doesn't change the result of g().
> Actually, I find this _Bad_. Obviously, the func's behaviour and result depend on arbitrary external values (referentially opaque). What do you think?
That is always the case when a function accesses globals. Globals are
_Bad_, yes.
Kent
More information about the Tutor
mailing list