AnswerBun.com

Weird scoping behavior in python

Stack Overflow Asked by snickerdoodles777 on December 8, 2020

Consider the following snippet of python code:

x = 1
class Foo:
    x = 2
    def foo():
        x = 3
        class Foo:
            print(x) # prints 3
Foo.foo()

As expected, this prints 3.
But, if we add a single line to the above snippet, the behavior changes:

x = 1
class Foo:
    x = 2
    def foo():
        x = 3
        class Foo:
            x += 10
            print(x) # prints 11
Foo.foo()

And, if we switch the order of the two lines in the above example, the result changes yet again:

x = 1
class Foo:
    x = 2
    def foo():
        x = 3
        class Foo:
            print(x) # prints 1
            x += 10
Foo.foo()

I’d like to understand why this occurs, and more generally, understand the scoping rules that cause this behavior. From the LEGB scoping rule, I would expect that both snippets print 3, 13, and 3, since there is an x defined in the enclosing function foo().

One Answer

Class block scope is special. It is documented here:

A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope.

Basically, class blocks do not "participate" in creating/using enclosing scopes.

So, it is actually the first example that isn't working as documented. I think this is an actual bug.

EDIT:

OK, so actually, here's some more relevant documentation from the data model, I think it all is actually consistent with the documentation:

The class body is executed (approximately) as exec(body, globals(), namespace). The key difference from a normal call to exec() is that lexical scoping allows the class body (including any methods) to reference names from the current and outer scopes when the class definition occurs inside a function.

So class blocks do participate in using enclosing scopes, but for free variables (as is normal anyway). In the first piece of documentation that I'm quoting, the part about "unbound local variables are looked up in the global namespace" applies to variables that would normally be marked local by the complier. So, consider this notorious error, for example:

x = 1
def foo():
    x += 1
    print(x)

foo()

Would throw an unbound local error, but an equivalent class definition:

x = 1
class Foo:
    x += 1
    print(x)

will print 2.

Basically, if there is an assignment statement anywhere in a class block, it is "local", but it will check in the global scope if there is an unbound local instead of throwing the UnboundLocal error.

Hence, in your first example, it isn't a local variable, it is simply a free variable, and the resolution goes through the normal rules. In your next two examples, you us an assignment statemnt, marking x as "local", and thus, it will be looked up in the global namespace in case it is unbound in the local one.

Correct answer by juanpa.arrivillaga on December 8, 2020

Add your own answers!

Related Questions

Red box isn’t placed as expected

1  Asked on November 4, 2021 by liya-thomas

       

String Comparison Javascript

2  Asked on November 4, 2021 by crimsonpython24

       

es5 using promise but returns defered before resolving

2  Asked on November 4, 2021 by rodney-wormsbecher

     

2 arrays with json problem (Node.js, Discord.js)

3  Asked on November 4, 2021 by mr-code

     

Is there a XAML equivalent to nameof?

2  Asked on November 4, 2021 by luxun

       

Multistage builds with yum

3  Asked on November 4, 2021 by vorticity

 

WooCommerce can’t see product variations

1  Asked on November 4, 2021 by tizio-fittizio

       

VScode setup with ROS and auto complete

3  Asked on November 4, 2021 by dixcipuli

         

Pick an account after Connect-MicrosoftTeams

4  Asked on November 4, 2021 by mauek-unak

   

Show local image file:///tmp/someimage.jpg

1  Asked on November 4, 2021 by dani-herrera

   

Ask a Question

Get help from others!

© 2023 AnswerBun.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP