Scope
LEGB Rules
Python search variable name by a hierarchy levels
  • Local can be inside a function or class method
  • Enclosed can be its enclosing function
  • Global refers to the uppermost level of the executing script itself
  • Built-in are special names that Python reserves for itself
  • One sentence rule: the reference of a variable is visible to all its lower levels, thus mutable variables are visible and able to be modified in the lower levels, immutable variables are visible in the lower levels, but not able to be modified since the modification will create a local variable instead of modify the variable in the upper levels

  • dir() will give you the list of in scope variables
  • globals() will give you a dictionary of global variables
  • locals() will give you a dictionary of local variables
  • n = 10; #global variables
    
    def f():
        m = 100;
        print(dir()) # ['m']
        print(globals()) # {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x108df2860>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 's1.py', '__cached__': None, 'n': 10, 'f': <function f at 0x108cf52f0>}
        print(locals()) # {'m': 100}
    
    print(dir()) # ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'n']
    f();
    			
    n = 10 # global variable
    print(id(n), n) # 4405450128 10
     
    # access local variable
    def f1():
        n = 1;
        print(id(n), n) # 4450449840 1, reference local variable n
     
    # access variable in enclosing function
    def f2():
        n = 2;
        def f3():
            print(id(n), n) # 4405449872 2, reference n defined in f2
        f3();
     
    # access global variable
    def f4():
        def f5():
            print(id(n), n) # 4405450128 10, reference global variable
        f5();
     
    f1();
    f2();
    f4();
    			
    Immutable
    a = 10 # global variable
    print(id(a), a) # 27193472, 10
     
    def f1():
    	# global variable is visible to the lower level
    	# globals: {'a': 10, ...}
    	print(id(a), a) # 27193472, 10
     
    def f2():
    	# create a local variable, which screens the global variable
    	# globals: {'a': 10, ...}
    	a = 100
    	# locals: {'a': 100}
    	print(id(a), a) # 27195296 100
     
    f1()
    f2()
    print(id(a), a) # 27193472, 10
    			
    Mutable
    l = list(range(10)) # global variable
    print(id(l), l) # 140678046472600, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     
    def f3():
    	# the reference of the gocal variable is visible to the lower level
    	print(id(l), l) # 140678046472600, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     
    def f4():
    	# modify an element of the global variable
    	l[1] = 100
    	print(id(l), l) # 140678046472600, [0, 100, 2, 3, 4, 5, 6, 7, 8, 9]
     
    f3()
    f4()
    print(id(l), l) # 140678046472600, [0, 100, 2, 3, 4, 5, 6, 7, 8, 9]
     
    def f5():
    	# create a local variable, which screens the global variable
    	l = [1, 2, 3, 4]
    	print(id(l), l) # 139718240373432, [1, 2, 3, 4]
     
    f5()
    print(id(l), l) # 140678046472600, [0, 100, 2, 3, 4, 5, 6, 7, 8, 9]
    			
    global keyword
    t = 10
    print(id(t), t) # 140417416870688, 10
     
    def f8():
    	global t # t is a global variable
    	print(id(t), t) #140417416870688, 10
    	t = 100 # modify the global variable t, create a new address, reference global variable to new address
    	print(id(t), t) #140249594380832, 100
    	print('globals: ', globals()) # globals: {'t': 100}
    	print('locals: ', locals()) # locals: {}
     
    f8()
    print(id(t), t) # 140249594380832, 100
    			
    import module
    def f():
        import math; # import math in local namespace
        print(math.pi)
     
    f();
    #print(math.pi) # not import math error
    			
    global variable among modules

  • global_list is imported once
  • global_list is shared by both module a and module b
  • # g.py
    global_list = [1, 2]
    print('g', id(global_list), global_list)
    			
    # a.py
    print('a ...')
    
    #import g
    from g import global_list
    
    def change():
        global_list.append(100)
    
    change()
    print('a', id(global_list), global_list)
    			
    # b.py
    print('b ...')
    
    #import g
    from g import global_list
    from a import *
    
    def display():
        print('display', id(global_list), global_list)
    
    print('b', id(global_list), global_list)
    
    change()
    display()
                
    # python b.py
    b ... # start module b
    g 140597500568384 [1, 2] # import module g
    a ... # import module a
    a 140597500568384 [1, 2, 100] # global_list in a
    b 140597500568384 [1, 2, 100, 100] # global_list in b
                

  • global_list is imported once, no need to import in b again
  • # g.py
    global_list = [1, 2]
                
    # a.py
    print('a ...')
    
    from g import global_list
    
    print('a', id(global_list), global_list)
                
    # b.py
    print('b ...')
    
    from a import *
    
    print('b', id(global_list), global_list) # global_list is available in b.py
                
    Resource
  • Visibility of global variables in imported modules
  • Beginner's Guide
  • Namespace at Python Course