Search code examples
pythonpython-3.xglobal-variableswith-statement

Why doesn't the with block in Python need the global keyword?


I am using the with block to extract information from a file, and I am surprised that any variables declared in the with block are global. Shouldn't they be local to the with block, and shouldn't you need to use global myVar before changing global variables? After all, with defines a code block, right?


Solution

  • Python doesn't have block scopes. The with statement does not introduce a new scope; the body of the statement is still in the same scope that has the with statement.

    Python has 4 kinds of scopes:

    1. The built-in scope, defining names available in any module without an explicit import.
    2. Global scopes, one per module.
    3. Non-local scopes
    4. Local scopes: defined by a function.

    No other construct defines a new scope: not if statements, not for or while loops, not with statements, not try statements, not class statements. Only things that define new functions (def statements, lambda expressions, and comprehensions) create new (local) scopes.

    Every name first looks in the local scope (which may be the global scope if not inside a function definition), then in any non-local scopes (which may not exist, if a function is defined in the global scope, not another local scope), then the global scope, and finally in the built-in scope.


    A non-local scope is just a scope that isn't the current local scope. For a module defined at the global scope, the closest enclosing non-local scope is the global scope.

    But if a function is defined inside another function, then the closest enclosing non-local scope is the local scope in which the function is defined.

    Functions can be nested fairly deeply, so there could be 0 or more additional local scopes between the current local scope and the global scope in which name lookups can occur. For example,

    x1 = 'a'
    
    def f1():
        x2 = 'b'
        def f2():
            x3 = 'c'
            def f3():
                x4 = 'd'
                print(x1 + x2 + x3 + x4)
            f3()
        f2()
    
    f1()
    

    The output of this mess would be abcd. When the argument to the print statement requires values for each of the four variables, each lookup starts in the local scope. Only a value for x4 is found there; the other lookups extend into the nearest enclosing non-local scope, that of f2. A value for x3 is found there, so the lookup for x1 and x2 extend to the next non-local scope, that of f1. x2 is found, so f1 extends another stop. Finally, a value for x1 is found in the global scope.

    So a non-local scope isn't so much a special kind of scope, just a name for a scope that isn't local to the currently executing function. It will either be the local scope of an enclosing function or the global scope.