Using the Python locals() function

Python Locals

Today, we’ll be looking at using the Python locals() function. This is yet another utility function which is quite useful for debugging your program.

The locals() function gives us the current local symbol table, as a dictionary.

Now, if you’re unsure as to what exactly a local symbol table is, keep reading. Let’s go step by step, starting with the definition of a Symbol Table.


What is a Symbol Table?

Well, a Symbol Table is a table consisting of information regarding different symbols. Here, a symbol could mean anything – a variable name, a keyword, a function name, etc.

They represent all the names of all variables, classes, and functions in your program.

Generally, a Symbol Table not only consists of the names of these objects, but other useful information as well, like the type of the object, the scope, etc.

Now that you know what a Symbol Table means, let’s come to the classes of Symbol Tables.

There are 2 types of Symbol Tables, for a Python program:

  • Global Symbol Table -> stores information related to the global scope of the program
  • Local Symbol Table -> stores information related to the local (current) scope of the program

These are the two symbol tables, defined based on the global scope and the local (current) scope.

When we refer to a Local Symbol Table, we refer to all the information in our current scope, when the interpreter executes our code line by line.

What does the Python locals() function exactly do?

Now, what the locals() function does is to simply paste the Local Symbol Table information on the console, on that scope where locals() was called from!

So this naturally means that the output of Python locals() will be a dictionary of all the variables names and attributes, scope, etc.

For example, if you have a file called main.py. Let’s put locals() as our only statement, and see what happens. We should get all related information in the main scope (in this case, it is the same as the Global Scope)

# main.py
print(locals())

Possible Output

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x12ba85542>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/askpython/home/locals_example.py', '__cached__': None}

Well, we could see some attributes of the main module (Global Scope), which includes some package details as well!

As some of you may immediately realize, this is the same as globals() here, since both are referring to the same global scope.


Calling locals() from a local scope

Now, let’s consider calling locals() inside a local scope, from a function.

Calling locals() inside a function

Let’s consider a simple function fun(a, b), that takes two arguments a and b, and returns the sum. We’ll call locals() just before the function returns.

# Global variable
global_var = 1234

def fun(a, b):
    global global_var
    temp = 100
    print(f"locals() inside fun(a, b) = {locals()}")
    return a + b

if __name__ == '__main__':
    print(f"locals() outside fun(a, b) = {locals()}")
    print(fun(10, 20))

Output

locals() outside fun(a, b) = {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f7023e1ff60>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'locals_example.py', '__cached__': None, 'global_var': 1234, 'fun': <function fun at 0x7f7023e5b1e0>}
locals() inside fun(a, b) = {'a': 10, 'b': 20, 'temp': 100}
30

Here, there is a noticeable change from within fun(a, b). Here, the local symbol table consists only of the names related to this function.

Since a local scope is not part of a package, there is no package information, and this only contains variables related to the function, plus the arguments.

Also notice that the global variable global_var is a part of the global symbol table, and is not present in the local symbol table as a result!

Calling locals() inside a Class

This is similar to calling from a function, but this will contain all the class methods and relevant attributes.

Let’s quickly take a look, using an example.

class Student():
    def __init__(self, name):
        self.name = name
    def get_name(self):
        return self.name
    print(f"Calling locals() from inside a class => {locals()}")
    print(f"Calling globals() from inside a class => {globals()}")

if __name__ == '__main__':
    s = Student('Amit')
    print(s.get_name())

Here, we’ll be calling locals() inside the Class after we define all the class methods. So these class methods must also be a part of the local symbol table.

Output

Calling locals() from inside a class => {'__module__': '__main__', '__qualname__': 'Student', '__init__': <function Student.__init__ at 0x7fe2672f0c80>, 'get_name': <function Student.get_name at 0x7fe2672f0d08>}

Calling globals() from inside a class => {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fe2673cff28>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'locals_class.py', '__cached__': None}
Amit

Indeed, as you can see, locals() does give the relevant methods!

When we call locals() from within the class body, we’ll get the module name, class name and class variables.

The global symbol table does not have anything of this sort, as expected.


Conclusion

In this article, we learned how we can get information from the local scope, using the locals() function. This returns a dictionary of all useful names and attributes from the local symbol table, and is very useful for debugging.

References