Re: Some python comments/questions

Guido.van.Rossum@cwi.nl
Wed, 13 Oct 1993 10:28:45 +0100

> Note that "f" in both functions is a local variable, so you're not
> really creating new symbols. You need to "return eval('f')", instead
> of "return f" in my python version, since "f" is not known to be a
> local variable statically (you could also do an initial "f = None",
> to avoid having to "eval('f')").

This illustrates an interesting fine point of Python's eval/exec
semantics: if exec() introduces a new name in the local namespace, the
"compiler" can't know about it and any references to it later will
fail mysteriously (since it actually exists in the dictionary of local
variables but the generated pseudo code will use a "global variable
reference" and fail to find it). For example, if you write

i = 1

def func():
j = 1
return i, j

then the "compiler" (which generates pseudo code for the Python
interpreter) sees that there is a local variable j bot no local
variable i, so it will generate a local reference for returning j but
a global reference for returning i. The reason for doing so is that
this speeds up both kinds of references: global references don't first
have to do an unsuccessful lookup in the dictionary of local
variables, and (starting in 0.9.9) local references are speeded up by
using an index into a list instead of a dictionary lookup. Note that
any assignment by definition creates a local variable unless
explicitly declared global first -- 'global' is in fact one of the few
statements that is interpreted at compile time instead of at run-time.
Also note that for this purpose the compiler keeps track of all local
variables, but not of all global variables. Finally, for the really
inquiring minds, built-in functions (e.g. len()) and names (i.e. None)
are treated as globals here and they *do* suffer one unsuccessful
lookup in the table of globals. This means you can override built-in
names with local or global names. You can even change the set
of built-in functions by modifying the built-in module 'builtin' (soon
to be renamed to '__builtin__' to emphasize its special status, like
'__main__').

The effect of all this is that, if the only assignment to a variable
is hidden inside an exec(), the compiler can't know about it at the
time the containing code is being compiled and it will thus assume it
is a global variable. (The exec() can't modify the containing code --
it only generates new code for what is passed to it.)

My preferred solution is indeed an initial assignment "f = None". You
deserve credit for inventing the alternative "return eval('f')".
However, though shorter in lines of code, involves the overhead of
parsing the string 'f' on each execution.

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>