[Tutor] python closures

Hugo Arts hugo.yoshi at gmail.com
Mon Nov 30 17:19:20 CET 2009


On Mon, Nov 30, 2009 at 11: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?
> 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?

A real closure is indeed created, but you are missing something.
Consider this python session:

 >>> x = 0
 >>> def f():
		x = x + 1

 >>> f()

Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    f()
  File "<pyshell#26>", line 2, in f
    x = x + 1
UnboundLocalError: local variable 'x' referenced before assignment

The python docs offers some insight:

The execution of a function introduces a new symbol table used for the
local variables of the function. More precisely, all variable
assignments in a function store the value in the local symbol table;
whereas variable references first look in the local symbol table, then
in the local symbol tables of enclosing functions, then in the global
symbol table, and finally in the table of built-in names. Thus, global
variables cannot be directly assigned a value within a function
(unless named in a global statement), although they may be referenced.

( from http://docs.python.org/tutorial/controlflow.html#defining-functions )

In short, the problem is that writing "x =" will create a new
(unbound) local name for x, hiding the global one. The following
reference to x will find the local unbound variable and start
complaining.

If you wanted to create a local variable, your idiom is the fix you
want. If you want to modify the global one, use the 'global x'
statement to tell the interpreter that explicitly.

> 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.
> Actually, I find this _Bad_. Obviously, the func's behaviour and result depend on arbitrary external values (referentially opaque). What do you think?

I don't quite understand the point you're trying to make. The code
you're showing above seems like what is 'proper' behaviour to me. Can
you show an example demonstrating why it is bad?

Hugo


More information about the Tutor mailing list