4

In my Python3 learning, while trying out globals() and locals(), I made a very basic, stand-alone Python program and received results for which I ask for expert explanation.

I then executed the same 2 functions inside my Python program (any) and received a list of many (all ?) values in my program, regardless if it was declared local or global or non. My understanding was that the globals() functions holds a list of all values that were declared as globals (same for locals), but according to my observation, the results show something different. Can anyone explain what I see and why?

Here is the program and the results:

The python program:

 print("Globals=",globals())
 print("Locals=",locals())

The result (for the above 2-lines program):

=============== RESTART: /home/pi/Junk/globals_locals_test.py ==========
Globals= {'__package__': None, '__spec__': None, '__loader__': <class '
_frozen_importlib.BuiltinImporter'>, '__doc__': None, '__file__': '
/home/pi/Junk/globals_locals_test.py', '__builtins__': <module 'builtins'
 (built-in)>, '__name__': '__main__'}
Locals= {'__package__': None, '__spec__': None, '__loader__': <class '
_frozen_importlib.BuiltinImporter'>, '__doc__': None, '__file__': '
/home/pi/Junk/globals_locals_test.py', '__builtins__': <module 'builtins'
 (built-in)>, '__name__': '__main__'}
>>>
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
samtal
  • 131
  • 2
  • 12
  • Possible duplicate of [difference between locals() and globals() and dir() in python](https://stackoverflow.com/questions/32003472/difference-between-locals-and-globals-and-dir-in-python) –  Sep 13 '18 at 10:51
  • Yes, this is a very detailed explanation that partially answers my question, mainly the 'hows' but not the 'whys'. – samtal Sep 13 '18 at 12:17

1 Answers1

6

Simple explanation

globals() refers to the current modules' attribute dictionary. locals() refers to the current local variables in your function/code-snippet.

Setting a variable will only ever change locals(). (Unless you tell python otherwise using the global or nonlocal keyword.)

Here an example

By default on module-scope globals is the same dict as locals:

>>> globals() is locals()
True

Since globals is locals in this case, modifying the locals will also modify the globals.

If you create a function and look at locals in there, you will see that locals will differ

>>> def test():
...    print("globals is locals:", globals() is locals())
...    print("globals:", globals())
...    print("locals:", locals())
>>> test()
globals is locals: False
globals: {'__name__': '__main__', ...}
locals: {}

Locals will automatically update when you change a function-local variable

>>> def test2():
...     print("locals 1:", locals())
...     x = 1
...     print("locals 2:", locals())
>>> test2()
locals 1: {}
locals 2: {'x': 1}

Something similar happens when creating new classes

>>> class Test:
...     print("locals:", locals())
locals: {'__module__': '__main__', '__qualname__': 'Test'}

More in-depth explanation

If you want to know why globals and locals are the way they are let's look at what happens under the hood of Python.

Some ground work

All python code passes what equates to the eval or exec function at some point. These functions accept three parameters: source, globals (defaults to current globals) and locals (defaults to current locals).

The function globals() and locals() will return whatever has been passed into the eval or exec functions shown above.

What does the Python Shell do?

If you do

>>> print(globals())

The REPL will internally do something along the lines of

# This variable stores your globals.
_my_main_module = {}

def exec_some_line(line):
    return eval(line, globals=_my_main_module, locals=_my_main_module)

# ...

exec_some_line("print(globals())")

As you can see, the Python Shell will at some point set globals and locals to the same dict.

Function execution

Internally, function execution will essentially do three things:

  1. Parse the arguments passed to the function and add them to the local variables.
  2. Execute the code of the function
  3. Return its result.

Here a pseudo-algorithm:

def __call__(*args, **kwargs):
    local_variables = parse_signature_with_args(args, kwargs)
    exec(function_source, function_globals, local_variables)
    return function_result

Creating new classes

When using the class-statement, all indented code will be executed separately.

  1. a new dictionary is created that will act as locals()
  2. Your code is executed with said locals.
  3. The class is created passing locals in

If you execute this code:

class Test:
   a = 5

This is approximately what happens:

 # 1. A new dictionary is created
 _dict = type.__prepare__()
 _dict["__module__"] = __name__
 _dict["__qualname__"] = "Test"

 # 2. Execute the code
 exec("a = 5", globals=globals(), locals=_dict)

 # 3. A class is created
 Test = type("Test", (), _dict)

How this maps to module imports

If you import a module an intricate import mechanism starts. This is a simplified overview:

  1. The interpreter will look if the module has already been imported.
  2. The interpreter will find the file.
  3. Then the file is read and parsed
  4. A module object is created.
  5. The python script is executed and its globals and locals will be set to the new modules' __dict__ attribute.
  6. The module object is returned.

It works something like this:

import sys
from types import ModuleType
def __import__(name):
    # 1. See if module is already imported
    if name in sys.modules:
       return sys.modules[name]

    # 2. Find file.
    filename = find_out_path_to_file(name)

    # 3. Read and parse file
    with open(filename) as f:
      script = f.read()

    # 4. Create the new module
    module = ModuleType(name)

    # 5. Execute the code of the module.
    exec(script, globals=module.__dict__, locals=module.__dict__)

    # 6. Return the new module.
    return module
StuxCrystal
  • 846
  • 10
  • 20
  • Excellent explanation. Thanks. Although it will require deeper investigation and more time to digest the details, it answers my question. – samtal Sep 13 '18 at 12:22
  • Thank you. Please accept my answer if it has helped you. – StuxCrystal Sep 13 '18 at 12:31
  • 2
    Python doesn't actually use the `locals()` dict for local variable lookup most of the time, though. Inside a function, writes to `locals()` won't affect local variables, and writes to local variables won't affect the local variable dict until you call `locals()` again. – user2357112 Nov 19 '18 at 22:55
  • Interesting observation. Didn't think of that. It becomes especially clear when looking at the disassembled function. Good catch. Have a +1. – StuxCrystal Nov 20 '18 at 07:02