Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python class and global vs local variables [duplicate]

I have a problem understanding what is happening with the outcome of the following pieces of code:

my_str = "outside func"
def func():
    my_str = "inside func"
    class C():
        print(my_str)
        print((lambda:my_str)())
        my_str = "inside C"
        print(my_str)

The output is:

outside func
inside func
inside C

Another piece of code is:

my_str = "not in class"
class C:
    my_str = "in the class"
    print([my_str for i in (1,2)])
    print(list(my_str for i in (1,2)))

The output is:

[‘in the class’, 'in the class’]
['not in class’, 'not in class’]

The question is:

  1. What is happening here in each print() statement?
  2. Can anyone explain why the print() statement get the string from the different namespaces?

Edit 1:

I think this is different from this question because I humbly think the answers there do not explain this variation:

my_str = "outside func"
def func():
    my_str = "inside func"
    class C():
        print(my_str)
        print((lambda:my_str)())
       #my_str = "inside C"
        print(my_str)

The output is:

inside func
inside func
inside func

Edit 2:

Indeed, this is a duplicate from this question because as Martijn Pieters says:

The answer there states: If a name is assigned to within a class body, almost at the start. You assigned to my_str, making it the same case as y there. Commenting out that line means you are no longer assigning to my_str, making it the same case as x.


like image 613
Dargor Avatar asked Jun 04 '15 14:06

Dargor


Video Answer


1 Answers

There are four scopes here:

  1. the global scope
  2. the function scope
  3. the class body
  4. the lambda scope

When creating a class statement, the class body is executed as a function would and the local namespace of that 'function' is used as the class attributes.

However, a class body is not a scope, and as such behaves differently from functions. In a function body, binding defines scope; assign to a name in a function and it is marked as a local. In a class, assigning to a name makes it a global until you actually do the assignment.

So your first print() call finds my_str as a global, because later in the class body you bind to the name. This is a consequence of class bodies not taking part in scopes.

Next, you define a lambda and call it. my_str is never assigned to in that lambda so it has to be found in a parent scope. Here the class body is not a scope, and the next scope up is the function scope. This is why that line prints inside func.

Last, you assign to the local name my_str in the class body, and print that local.

The moment you remove the assignment, the names in the class are treated as non-local; the first and last print() statements are now equal, and the name is simply looked up according to the normal scoping rules.

like image 142
Martijn Pieters Avatar answered Sep 23 '22 20:09

Martijn Pieters