Namespaces and Scopes in Python

Namespaces in Python

by Leodanis Pozo Ramos Apr 14, 2025 intermediate python

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Navigating Namespaces and Scope in Python

A Python namespace is a mapping from names to objects. It works like a dictionary where keys are object names and values are the objects themselves. Namespaces organize variables and functions in a dedicated space, allowing you to use multiple instances of the same name without conflict, as long as they’re in different namespaces.

In this tutorial, you’ll explore the different types of namespaces in Python, including the built-in, global, local, and enclosing namespaces. You’ll also learn how they define the scope of names and influence name resolution in Python programs.

By the end of this tutorial, you’ll understand that:

  • Python namespaces serve as containers that map names to objects, allowing for organized access and management of variables, functions, classes, and objects in general.
  • Namespace and scope differ in that a namespace maps names to objects, while a scope is the region of code where you can access a name.
  • Python implements most namespaces using dictionaries, where each namespace’s lifecycle is tied to the execution context, such as global or local scopes.

Understanding how namespaces work will improve your ability to manage and organize code efficiently in Python programs, helping to prevent name conflicts and other issues. To get the most out of this tutorial, you should be familiar with Python variables and functions. Knowledge of inner functions and closures will also be a plus.

Take the Quiz: Test your knowledge with our interactive “Namespaces in Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Namespaces in Python

In this quiz, you'll test your understanding of Python namespaces. These concepts are crucial for organizing the symbolic names assigned to objects in a Python program and ensuring they don't interfere with one another.

Getting to Know Namespaces in Python

A namespace is a container that holds the currently defined symbolic names and the objects each name references. You can think of a namespace as a dictionary, in which the keys are object names and the values are the objects themselves. Each key-value pair maps a name to its corresponding object.

Namespaces let you use the same name in different contexts without collisions. It’s like giving everything its own little room in a well-organized house. They allow Python to keep things organized, prevent naming conflicts, support the concept of scope, and enforce modularity.

Namespaces are so crucial in Python that they were immortalized in The Zen of Python:

Namespaces are one honking great idea—let’s do more of those!

Tim Peters

As Tim Peters suggests, namespaces aren’t just great. They’re honking great, and Python uses them extensively. Depending on how you structure your code, a Python program can have up to four different types of namespaces:

  1. Built-In
  2. Global
  3. Local
  4. Enclosing or nonlocal

These namespaces have differing lifetimes. As Python executes a program, it creates namespaces as necessary and removes them when it no longer needs them. Typically, many namespaces will exist at any given time.

The Python global, local, and nonlocal namespaces are implemented as dictionaries. In contrast, the built-in namespace isn’t a dictionary but a module called builtins. This module acts as the container for the built-in namespace.

In the following sections, you’ll learn about these four namespaces and what their content and behavior are.

The Built-in Namespace

The built-in namespace contains the names of all of Python’s built-in objects. This namespace is available while the Python interpreter is running. So, you can access the names that live in this namespace at any time in your code without explicitly importing them.

You can list the objects in the built-in namespace with the dir() function using the __builtins__ object as an argument:

Python
>>> dir(__builtins__)
[
    'ArithmeticError',
    'AssertionError',
    'AttributeError',
    'BaseException',
    ...
    'super',
    'tuple',
    'type',
    'vars',
    'zip'
]

You may recognize some objects here, such as built-in exceptions, built-in functions, and built-in data types. Python creates the built-in namespace when it starts and keeps it active until the interpreter terminates.

The Global Namespace

The global namespace contains the names defined at the module level. Python creates a main global namespace when the main program’s body starts. This namespace remains in existence until the interpreter terminates.

Additionally, each module has its own global namespace. The interpreter creates a global namespace for any module that your program loads with the import statement. For further reading on main functions and modules in Python, see the following resources:

For now, when you see the term global namespace, you can think of it as the one belonging to the main program.

To illustrate, get back to your REPL session and run the following code:

Python
>>> number = 42
>>> dir()
[
    '__annotations__',
    '__builtins__',
    '__cached__',
    '__doc__',
    '__file__',
    ...
    'number'
]

In this code snippet, you define the number variable as a global name. Then, you call the built-in dir() function to check the list of names defined in your current global scope. At the end of the list, you’ll find the 'number' key, which corresponds to your global variable.

The Local Namespace

The Python interpreter creates a new and dedicated namespace whenever you call a function. This namespace is local to the function and exists only until the function returns:

Python
>>> def double_number(number):
...     result = number * 2
...     print(dir())
...     return result
...

>>> double_number(4)
['number', 'result']
8

>>> result
Traceback (most recent call last):
    ...
NameError: name 'result' is not defined

>>> number
Traceback (most recent call last):
    ...
NameError: name 'number' is not defined

In this example, the number argument and the result variable are local to double_number(). Note that if you try to access them after the function has returned, you get NameError exceptions.

The Enclosing or Nonlocal Namespace

You can also define one function inside another. In this case, you’d have what’s known as an inner function. In the example below, you have the three scopes:

Python
 5>>> global_variable = "global"
 6
 7>>> def outer_func():
 8...     # Nonlocal scope
 9...     nonlocal_variable = "nonlocal"
10...     def inner_func():
11...         # Local scope
12...         local_variable = "local"
13...         print(f"Hi from the '{local_variable}' scope!")
14...         print(f"Hi from the '{nonlocal_variable}' scope!")
15...         print(f"Hi from the '{global_variable}' scope!")
16...     inner_func()
17...
18
19>>> outer_func()
20Hi from the 'local' scope!
21Hi from the 'nonlocal' scope!
22Hi from the 'global' scope!

In this example, you first create a global variable at the module level. Then, you define a function called outer_func(). Inside this function, you have nonlocal_variable, which is local to outer_func() but nonlocal to inner_func(). In inner_func(), you create another variable called local_variable, which is local to the function itself.

Each of these namespaces remains in existence until its respective function returns. Python might not immediately reclaim the memory allocated for those namespaces when their functions terminate, but all references to the objects they contain become invalid.

Understanding the Scope of Names

The existence of multiple, distinct namespaces allows you to have several different instances of a particular name simultaneously while a Python program runs. As long as each instance is in a different namespace, they’re all maintained separately and won’t interfere with one another.

That raises a question. Suppose you refer to the name x in your code, and x exists in several namespaces. How does Python know which one you mean each time?

The answer lies in the concept of scope. The scope is the region of a program in which a name has meaning. The interpreter determines this at runtime based on where the name definition occurs and where the name is referenced in the code.

The concepts of namespace and scope are closely related. In practice, namespaces are how Python applies the concept of scope to the name lookup process.

The LEGB Rule for Searching Names

Returning to the question from the previous section, say that you’re referencing x from inside an inner function. In this situation, Python looks for x in the following order:

  1. Local: Python searches for x inside the inner function. If it doesn’t find x there, then it moves to the enclosing scope.
  2. Enclosing: Next, Python searches for x in the enclosing function’s scope. If it’s not found there, then it moves to the global scope.
  3. Global: Python searches for x in the global scope. If it still doesn’t find it, it moves to the built-in scope.
  4. Built-in: Finally, Python searches for x in the built-in scope. If it doesn’t find x there, then it raises a NameError exception.

This is what’s known as the LEGB rule. The interpreter searches for a name from the inside out, looking in the local, enclosing, global, and finally, the built-in scope:

Diagram of Local, Enclosed, Global, and Built-in Scopes
The LEGB Rule in Python

If Python doesn’t find the name in any of these namespaces, then it raises a NameError exception, as you’ve already learned. In the following sections, you’ll explore some examples that demonstrate how the LEGB rule works in practice.

The LEGB Rule in Action

In the example below, you define the x variable outside both outer() and inner() so it resides in your current global scope:

Python
>>> x = "global"

>>> def outer():
...     def inner():
...         print(x)
...     inner()
...

>>> outer()
global

The call to print() can only refer to one possible x. It displays the x object defined in the global namespace, which holds the string "global".

In the next example, you define x in two places: once in the global scope and once inside the outer() function:

Python
>>> x = "global"

>>> def outer():
...     x = "enclosing"
...     def inner():
...         print(x)
...     inner()
...

>>> outer()
enclosing

As in the previous example, inner() refers to x, but this time, it has two definitions to choose from:

  1. x in the global scope
  2. x in the enclosing scope

According to the LEGB rule, the interpreter finds the value from the enclosing scope before looking in the global scope. So print() displays enclosing instead of global.

Next, you define x everywhere. The first definition is in the global scope. The second is inside outer() but outside inner(). The third one is inside the inner() function:

Python
>>> x = "global"

>>> def outer():
...     x = "enclosing"
...     def inner():
...         x = "local"
...         print(x)
...     inner()
...

>>> outer()
local

Now, print() has to distinguish between three different possibilities:

  • The x in the global scope
  • The x in the enclosing scope
  • The x in the scope that’s local to inner()

In this case, the LEGB rule dictates that inner() first sees its own locally defined value of x, so the print() function displays local.

Next, you have a situation where inner() tries to print the value of x, but x isn’t defined anywhere. This results in an error:

Python
>>> def outer():
...     def inner():
...         print(x)
...     inner()
...

>>> outer()
Traceback (most recent call last):
    ...
NameError: name 'x' is not defined

This time, Python doesn’t find x in any of the namespaces, so the print() function generates a NameError exception.

Shadowing Built-in Names in Python

Shadowing or overriding names from the built-in namespace can be a common issue for beginners in Python. Built-in names are always available, and some could be suitable for real-world variables.

For example, say that you’re learning about lists and run the following code:

Python
>>> list = [1, 2, 3, 4]
>>> list
[1, 2, 3, 4]

In this example, you’ve used list as the name for a list object containing some numbers. This assignment overrides the built-in list() function:

Python
>>> list(range(10))
Traceback (most recent call last):
    ...
TypeError: 'list' object is not callable

Now, calling list() fails because you’ve overridden the name in your previous code. A quick fix for this issue is to use the del statement to remove the custom variable and recover the original name:

Python
>>> del list  # Remove the redefined name
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

If you accidentally reassign a built-in name, then you can run a quick del name statement to remove the redefinition from your scope and restore the original built-in name in your working scope.

A more reliable approach would be to import the builtins module and use fully qualified names:

Python
>>> list = [1, 2, 3, 4]

>>> import builtins
>>> builtins.list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

This time, even though you’ve overridden the list name, you can still use the original built-in function because of the fully qualified name builtins.list(), which unambiguously refers to the object in the built-in namespace.

Managing Namespace Dictionaries

Python provides two built-in functions, globals() and locals(), that allow you to access the global and local namespace dictionaries. These functions give you direct access to the content of both namespaces, which are implemented as dictionaries.

In the following sections, you’ll learn how to use globals() and locals() in your code. You’ll also get to know a critical difference between these built-in functions.

The globals() Function

The built-in globals() function returns a reference to the current global namespace dictionary. You can use it to access the objects in the global namespace. Here’s what it looks like when you start a REPL session:

Python
>>> type(globals())
<class 'dict'>

>>> globals()
{
    '__name__': '__main__',
    '__doc__': None,
    '__package__': '_pyrepl',
    ...
}

As you can see, the Python interpreter automatically adds several entries in globals(). Depending on your Python version and operating system, this may look a little different in your environment, but it should be similar.

Now, see what happens when you define a variable in the global scope:

Python
>>> number = 42
>>> globals()
{
    '__name__': '__main__',
    '__doc__': None,
    ...
    'number': 42
}

After the assignment statement number = 42, a new item appears in the global namespace dictionary. The dictionary key is the object’s name, number, and the dictionary value is the object’s value, 42.

You would typically access this object in the usual way by referring to its symbolic name, number. But you can also access it indirectly through the global namespace dictionary:

Python
>>> number
42
>>> globals()["number"]
42

>>> number is globals()["number"]
True

You can access the value of number using the dictionary key lookup syntax. The is operator in the final example confirms that these are the same object.

You can also use globals() to create and modify entries in the global namespace:

Python
>>> globals()["message"] = "Welcome to Real Python!"

>>> globals()
{
    '__name__': '__main__',
    '__doc__': None,
    ...
    'number': 42,
    'message': 'Welcome to Real Python!'
}
>>> message
'Welcome to Real Python!'

>>> globals()["message"] = "Hello, World!"
>>> message
'Hello, World!'

In this example, you first create a new global variable called message and assign a string to it. Then, you call globals() to inspect the namespace and find the new variable at the final position in the dictionary.

The locals() Function

Python also provides a built-in function called locals(). It’s similar to globals(), but it lets you access objects in the local namespace instead:

Python
>>> def func(x, y):
...     message = "Hello!"
...     print(locals())
...

>>> func(10, 0.5)
{'x': 10, 'y': 0.5, 'message': 'Hello!'}

When called within func(), locals() returns a dictionary representing the function’s local namespace. Notice that, in addition to the locally defined variables, the local namespace includes the function arguments x and y since these are also local to func().

When you call locals() outside a function in the main program, it behaves the same as globals(), returning a reference to the global namespace:

Python
>>> locals()
{
    '__name__': '__main__',
    '__doc__': None,
    '__package__': '_pyrepl',
    ...
    'func': <function func at 0x104c87c40>
}

>>> globals()
{
    '__name__': '__main__',
    '__doc__': None,
    '__package__': '_pyrepl',
    ...
    'func': <function func at 0x104c87c40>
}

>>> locals() is globals()
True

When you call locals() in the global namespace, you get a reference to the corresponding dictionary. In practice, locals() behaves the same as globals() in this context.

The Difference Between globals() and locals()

There’s one slight difference between globals() and locals() that’s useful to know about. The globals() function returns a reference to the dictionary that contains the global namespace. Because of this, if you call globals() and save its return value, then you can add new variables and modify existing ones as you’ve already learned.

In contrast, locals() returns a dictionary that’s a copy of the dictionary that holds the current local namespace rather than a reference to it. Because of this subtle difference, additions to the return value of locals() won’t modify the actual namespace:

Python
>>> def func():
...     message = "Hello!"
...     loc = locals()
...     print(f"{loc = }")
...     number = 42
...     print(f"{loc = }")
...     loc["message"] = "Welcome!"
...     print(f"{loc = }")
...     print(f"{locals() = }")
...

>>> func()
loc = {'message': 'Hello!'}
loc = {'message': 'Hello!'}
loc = {'message': 'Welcome!'}
locals() = {'message': 'Hello!', 'loc': {'message': 'Welcome!'}, 'number': 42}

In this example, loc points to the return value of locals(), which is a copy of the local namespace dictionary.

The assignment number = 42 adds a new variable to the local namespace but not to the copy that loc points to. Similarly, the assignment loc["message"] = 'Welcome!' modifies the value for key "message" in loc, but doesn’t affect the local namespace.

Finally, it’s worth noting that locals() returns a shallow copy of the namespace, so any name that points to a mutable object lets you change that object in place. This change is reflected in the local namespace:

Python
>>> def func():
...     fruits = ["apple", "banana"]
...     loc = locals()
...     loc["fruits"].append("orange")
...     print(f"{loc = }")
...     print(f"{locals() = }")
...

>>> func()
loc = {
    'fruits': ['apple', 'banana', 'orange']
}
locals() = {
    'fruits': ['apple', 'banana', 'orange'],
    'loc': {'fruits': ['apple', 'banana', 'orange']}
}

In this example, you keep a reference to the locals() return value in loc. Then, you use this variable to append a new value to the fruits list, which is mutable. This change affects the content of your local namespace, as you can see in the last highlighted line.

Modifying Variables From a Different Namespace

Sometimes, you try to modify a variable from an outer scope within a function. In a function, you can’t reassign a variable that points to an object defined on the outside and expect the change to affect that variable:

Python
>>> x = 20
>>> def f():
...     x = 40
...     print(x)
...

>>> f()
40
>>> x
20

Here, you have a global variable x with a value of 20. When f() executes the assignment x = 40, it creates a new local variable whose value is 40. Because of this reassignment, f() loses the reference to x in the global namespace. So, the assignment doesn’t affect the global variable but creates a new local variable.

Note that when f() executes print(x), it displays 40, the value of its own local x. But after f() terminates, the x variable in the global scope is still 20.

In contrast, a function can modify a mutable object from an outer scope:

Python
>>> fruits = ["apple", "banana", "cherry", "mango"]
>>> def f():
...     fruits[1] = "peach"
...

>>> f()

>>> fruits
['apple', 'peach', 'cherry', 'mango']

In this example, fruits is a list, and lists are mutable. You can change the list’s content inside f() even though the list is defined outside the function’s scope.

Again, if f() tries to reassign fruits entirely, then Python will create a new local object and won’t modify the global fruits:

Python
>>> fruits = ["apple", "banana", "cherry", "mango"]
>>> def f():
...     fruits = ["grapes", "orange"]
...

>>> f()

>>> fruits
['apple', 'peach', 'cherry', 'mango']

In this example, you assign a different list object to the name fruits inside the function. This creates a local variable that shadows the global one. Therefore, changes to the local fruits don’t affect the global fruits.

The global Statement

What if you need to modify a value in the global scope from within a function? You can do this using the Python global statement:

Python
>>> x = 20
>>> def f():
...     global x
...     x = 40
...     print(x)
...

>>> f()
40
>>> x
40

The global x statement indicates that while f() executes, references to the name x will refer to the x defined in the global namespace. That means the assignment x = 40 doesn’t create a new local variable. It assigns a new value to x in the global scope instead:

Example of Python global keyword usage
The global Statement

As you’ve already learned in previous sections, globals() returns a reference to the global namespace dictionary. If you want, you can produce the same result using globals() instead of the global statement:

Python
>>> x = 20
>>> def f():
...     globals()["x"] = 40
...     print(x)
...

>>> f()
40
>>> x
40

In this example, you directly modify the value of the global x using the globals() function and a dictionary key assignment. There isn’t a good reason to do it this way since global makes the code cleaner and more readable.

If the name specified in the global statement doesn’t exist in the global scope when the function starts, then a combination of the global statement and an assignment creates it:

Python
>>> y
Traceback (most recent call last):
    ...
NameError: name 'y' is not defined

>>> def f():
...     global y
...     y = 20
...

>>> f()
>>> y
20

In this case, when you call f(), there’s no object named y in the global scope. The global y statement declares y as global, and the assignment in the next line creates the variable in the global namespace for you. However, this is an unusual a way to create global variables and it’s not a recommended practice.

You can also specify several comma-separated names in a single global statement:

Python
>>> x, y, z = 10, 20, 30

>>> def f():
...     global x, y, z
...

Here, x, y, and z are all declared to refer to objects in the global scope by the single global statement on the highlighted line.

A name specified in a global statement can’t appear in the function body before the global statement:

Python
>>> def f():
...     print(x)
...     global x
...
  File "<python-input-0>", line 3
    global x
    ^^^^^^^^
SyntaxError: name 'x' is used prior to global declaration

In this example, the global statement makes x refer to an object in the global scope. The call to print() refers to x before the global statement, which raises a SyntaxError exception.

The nonlocal Statement

A similar situation occurs with nested functions when you need to modify variables in the enclosing scope. The nonlocal keyword addresses this by referring to variables in the nearest enclosing scope, which allows you to modify them within the nested function:

Python
>>> def f():
...     x = 20
...     def g():
...         nonlocal x
...         x = 40
...     g()
...     print(x)
...

>>> f()
40

After the nonlocal x statement, when g() accesses x, it refers to the x in the nearest enclosing scope, defined in the outer f() function:

Python nonlocal keyword example
The nonlocal Statement

The call to print() at the end of f() confirms that g() has changed the value of x in the enclosing scope to 40.

It’s important to note that the nonlocal keyword is only applicable in nested functions. You can only use it to access variables in the enclosing scope.

Conclusion

You’ve learned about Python namespaces, which map names to objects. You’ve also explored different types of namespaces, such as built-in, global, local, and enclosing, and learned how they define the scope of a name in Python.

Additionally, you delved into the LEGB rule, which defines how Python searches for names across the actual namespaces. You also learned how to modify variables from different scopes using global and nonlocal statements.

In this tutorial, you’ve learned:

  • What a namespace is in Python
  • How namespaces relate to the concept of scope
  • Which namespaces exist in Python, including the local, enclosing, global, and built-in
  • How Python applies the LEGB rule to resolve names in various scopes
  • How to manage namespace dictionaries using the globals() and locals() functions
  • How to modify variables from different scopes using global and nonlocal

With these skills, you can now write more organized and efficient Python programs to ensure your code remains clean and versatile.

Frequently Asked Questions

Now that you have some experience with Python namespaces and scopes, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click the Show/Hide toggle beside each question to reveal the answer.

A Python namespace is a container that holds symbolic names mapped to objects. It behaves similarly to a dictionary where keys are names and values are objects.

Python has built-in, global, local, and enclosing namespaces, each with its own purpose and scope within a program.

A namespace maps names to objects, while a scope is a region in your code where you can access a given name.

Python implements namespaces using dictionaries, with each namespace mapping names to objects within a particular scope.

The LEGB rule in Python stands for local, enclosing, global, and built-in. It describes the order in which Python searches for a name across different namespaces. This rule applies during the name resolution process.

Take the Quiz: Test your knowledge with our interactive “Namespaces in Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Namespaces in Python

In this quiz, you'll test your understanding of Python namespaces. These concepts are crucial for organizing the symbolic names assigned to objects in a Python program and ensuring they don't interfere with one another.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Navigating Namespaces and Scope in Python

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Leodanis Pozo Ramos

Leodanis is an industrial engineer who loves Python and software development. He's a self-taught Python developer with 6+ years of experience. He's an avid technical writer with a growing number of articles published on Real Python and other sites.

» More about Leodanis

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!