Python Forum

Full Version: Understanding Scoping in Python
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I was reading Python docs and encountered this example under "Scope and Namespaces".
def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)
The output to this code is
Output:
After local assignment: test spam After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam
My question is, why in output, it is
Output:
After global assignment: nonlocal spam
according to me it should be
Output:
After global assignment: global spam
as spam become a global variable after calling do_global(), and value becomes spam = "global spam"
Because scope_test already has local variable name spam.
scope looks up local spam first. If not exist then looks for global spam.

spam = "global"

def global_spam():
    print(spam)

def local_spam():
    spam = "local"
    print(spam)

local_spam()
global_spam()
(Aug-06-2018, 02:24 PM)Windspar Wrote: [ -> ]Because scope_test already has local variable name spam.
scope looks up local spam first. If not exist then looks for global spam.

spam = "global"

def global_spam():
    print(spam)

def local_spam():
    spam = "local"
    print(spam)

local_spam()
global_spam()


Hi Windspar,
Thanks for your explanation,but let me be more specific about my problem
I know that the scoping in python works by LEGB rule,

But when do_global() is called, control finds that do_global() is creating a global variable spam, which is priorly created in scope_test(). So why the variable spam in scope_test is not get overridden by spam variable of do_global(), as it happened when we called do_nonlocal() function (there value of spam = "test spam" became spam = "nonlocal spam",as you can see in the output)
Because global spam only brings in global scope variables. Not non local variables.

# This is global scope area
spam = "global scope"

def print_spam():
    print(spam)

# soon as you get into a function. It a local variable.
def spam_it():
    def update_spam():
        # grabs  spam from 1 layer down.
        nonlocal spam
        spam = "nonlocal"

    def update_global():
        global spam
        spam = "global scope change"

    print_spam()
    spam = "local"
    print(spam)
    update_spam()
    print(spam)
    update_global()
    print_spam()

spam_it()
(Aug-06-2018, 05:16 PM)Windspar Wrote: [ -> ]Because global spam only brings in global scope variables. Not non local variables.

# This is global scope area
spam = "global scope"

def print_spam():
    print(spam)

# soon as you get into a function. It a local variable.
def spam_it():
    def update_spam():
        # grabs  spam from 1 layer down.
        nonlocal spam
        spam = "nonlocal"

    def update_global():
        global spam
        spam = "global scope change"

    print_spam()
    spam = "local"
    print(spam)
    update_spam()
    print(spam)
    update_global()
    print_spam()

spam_it()


Okay. Thanks Windspar for your explanation. I understand now.
There are three different spam variables. A local spam, within do_local(), a local spam, within scope_test() (which is non-local for enclosed functions), and a global spam, which doesn't exist until after do_global() is called. do_global() creates a new variable in the global scope, but it doesn't change any of the other spams that already exist in other scopes. After do_global() is called, referencing spam uses the local version, which is set to "nonlocal spam" at that time.

If print(spam) were to return "global spam", that would violate scoping, as there's a local spam that exists.

...and that's why you avoid having variables with the same name. It adds unneeded confusion. It's highly unlikely that all three of those refer to the same thing, which means they're all poor choices for a variable name, as none actually describe what a spam is.