Scopes

Jeff Shannon jeff at ccvcorp.com
Tue Feb 5 13:27:58 EST 2002


dude at deletia.com wrote:

> Hi all,
>
> I've been learning C++ and have been turned onto Python
> but the quesiton i have is
> What is the advantage of pythons nested scope rules over c++ rules.
> If I read correctly
> If func B() has Func A() as its parent, then Func A() has access (can see)
> all of func B()'s variable.
> I am not sure of the advantage here, and would appreciate a little help

Hm, that doesn't sound *quite* right.  Let's start from the beginning, and
talk about Python's "classic" scoping, before we get into nested scopes.

Python (pre-2.2) keeps track of only three scopes--local, global, and
builtin.  (Note that "global" means module-scope, not application-wide scope;
if you import two different modules, each module has its own, independent,
global scope.)  Any time that it is trying to resolve a name, it searches them
in that order.  So, if you have a local name that's the same as a builtin name
(or global name), the local name will be found first, thus effectively
obscuring the builtin/global name.  Once you are out of that local scope, then
the builtin/global name becomes "visible" again.

Thus, you could have something like:

a = 5
def spam():
     a = 1
     print a
spam()  # prints 1
print a   # prints 5

This much seems pretty similar to C/C++ scoping.  The tricky part comes when
you have nested def's or classes (something which C/C++ doesn't allow):

def FunctionFactory(multiplier):
    value = 23
    def func():
        return value * multiplier
    return func
f = FunctionFactory(5)
f()

Now, you might expect that this should print 115, but it doesn't.  Within the
def func(), there is no local variable 'value'; there's also no global
variable 'value' either.  Python (in classic scoping) can't see what's in any
function except the most "current" one, and will thus raise a NameError
because it can't find 'value'.  The traditional workaround has been to modify
the func() definition to pass any needed locals as default parameters:

    def func(value=value):

But that then raises the possibility that someone might later call f(2), and
get 10 instead of 115.  Sometimes that might be desired, but other times it's
not.

Nested scopes is the solution to that quirk.  When nested scopes are enabled
(imported from __future__ in 2.1, or by default in 2.2+), then Python will
search for names not only in the current definition's local scope, but also in
the local namespace of any function/class that that definition is nested
inside of, and any namespace that *that* is nested inside of, etc.  (In
practice, I can't imagine any reason to need more than one level of nesting,
but I believe that nested scopes are recursive anyhow.)  Thus, now func() has
access to the local variables of FunctionFactory() (but not vice versa!) and
has no need for the questionable use of default values.

Hopefully this gives you a bit better of an idea of how namespacing works in
Python.  If you have any further questions, feel free to ask.

Jeff Shannon
Technician/Programmer
Credit International





More information about the Python-list mailing list