[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