Search⌘ K
AI Features

Imports and Namespaces

Explore how to organize your Python code by using imports and namespaces effectively. Learn the difference between importing entire modules versus selective imports, how to avoid naming conflicts with aliases, and understand the special __name__ variable for script versus module execution. This lesson helps you write modular, maintainable, and reusable Python programs by mastering code integration and namespace management.

We now understand that separating code into multiple files improves organization and reusability. However, dividing code is only part of the solution; those pieces must also be reconnected to form a working program.

Python excels at this through its batteries-included philosophy. It ships with an extensive standard library that provides ready-made modules for tasks such as mathematics, system interaction, date and time handling, and much more. In this lesson, we will learn how to bring these tools into our programs using the import statement.

We will also explore how Python manages namespaces to prevent naming conflicts, and how a file can determine whether it is being executed directly or imported as a module—allowing our code to behave appropriately in both situations.

The import statement and namespaces

A namespace is simply a mapping that connects names to the objects they refer to. When learning how to import a module in Python, it is important to understand that when a file runs, it has its own namespace where variables and functions are stored.

For example, when we write import math, Python does not pull all of the math functions directly into our file. Instead, it creates a single name, math, in the current namespace that refers to the math module as a whole. That module contains its own functions and values, such as sqrt, sin, and pi.

To use anything inside the module, we must explicitly access it through the name math, using dot notation. This is why we write math.sqrt() instead of just sqrt(). The functions belong to the module, not to our file.

Let’s see how to import the math module and access its contents.

Python
import math
# We access functions inside the 'math' namespace using the dot operator
root = math.sqrt(25)
print(f"The square root of 25 is {root}")
# We can also access constants defined in the module
area = math.pi * (5 ** 2)
print(f"Area of circle with radius 5: {area:.2f}")
# Attempting to use sqrt without the prefix raises an error
# print(sqrt(25)) # NameError: name 'sqrt' is not defined
  • Line 1: This loads the module but keeps the names inside the math container. It does not create a variable named sqrt in our global scope.

  • Line 4: We cross the namespace boundary using the dot operator (.). We are asking the math object to retrieve its internal sqrt function.

  • Lines 11–12: This demonstrates the safety of namespaces. Because we didn't import sqrt directly, Python doesn't recognize the name on its own, preventing accidental clashes with our own code.

Selective imports with from ... import ...

Sometimes we only need one or two specific items from a large module. We can import those items directly into our current namespace using the from ... import ... syntax.

This approach makes code more concise because we do not need to type the module name every time (e.g., sqrt(25) instead of math.sqrt(25)). However, we must be careful. Since these names now live in our global namespace, they can overwrite—or be overwritten by—our own variables. This is called "polluting the namespace."

Look at an example below.

Python
from random import randint
# 1. We use the imported function directly
print(f"Random roll: {randint(1, 6)}")
# 2. WARNING: Defining a local function with the same name
def randint(a, b):
return "I have overwritten the library function!"
# 3. The local definition now hides the imported one
print(f"New call: {randint(1, 6)}")
  • Line 1: This extracts randint from the random module and places it directly into our script's global scope. We no longer need the random. prefix.

  • Lines 7–8: We define a new function that happens to share the name randint. In Python, the most recent definition wins.

  • Line 11: When we call randint now, Python uses our local "fake" function instead of the standard library version. This is why explicit imports (keeping the module name) are often safer for large projects.

Aliasing with as

We might encounter modules with long names or names that conflict with variables we already have. Python allows us to create an alias (we a temporary nickname for the module or function) using the as keyword. This creates a reference with a new name while keeping the original object intact.

Common community standards often dictate specific aliases, such as import datetime as dt or import pandas as pd (in data science), making code easier to read for other developers.

Python
import datetime as dt
# We use the alias 'dt' instead of 'datetime'
current_time = dt.datetime.now()
print(f"Current full date and time: {current_time}")
print(f"Current year: {current_time.year}")
  • Line 1: We bind the datetime module to the name dt. The name datetime is not added to our namespace—only dt is.

  • Line 4: We access the module capabilities through the alias. This keeps code concise without losing the clarity of where the function came from (unlike from ... import).

How Python locates modules

When Python executes an import statement, it searches for the module in a specific order:

  1. Built-in modules: Python first checks if the module is part of the language (like math or sys).

  2. Current directory: It looks in the folder containing the script we are currently running.

  3. Standard library and installed packages: It checks the directories defined in sys.path (where Python is installed).

This search order leads to a common beginner mistake: naming a file identical to a standard library module. If we name our file math.py, Python will find our file before the real standard library math module. Any code trying to import math will load our script instead, causing errors when it tries to find functions like sqrt.

We can inspect this path to understand exactly where Python is looking.

Python
import sys
# Print the directories Python searches for modules
for path in sys.path:
print(path)
  • Line 1: We import the built-in sys module.

  • Line 4: We iterate over sys.path, which is just a standard Python list of strings.

  • Line 5: The first entry printed is usually the directory where our code is running. If our module is not in one of these folders, Python will raise a ModuleNotFoundError.

Script vs. module: The role of __name__

Every Python file can operate in two modes:

  1. As a script: Executed directly (e.g., clicking the file or running python file.py).

  2. As a module: Imported by another file.

Python uses a special built-in variable called __name__ to determine the current mode.

  • If the file is run directly, __name__ is set to "__main__".

  • If the file is imported, __name__ is set to the filename (the module name). We will see the examples of this in next lesson.

This distinction allows us to write code that runs only when the file is executed directly. This is crucial for adding tests or example usage to a module without those tests running every time the module is imported elsewhere.

Python
# Assume this code is saved in a file named 'calculator.py'
def add(a, b):
return a + b
print(f"Debug: The value of __name__ is '{__name__}'")
if __name__ == "__main__":
# This block ONLY runs if we execute this file directly
print("--- Running in Script Mode ---")
print(f"10 + 5 = {add(10, 5)}")
else:
# This block runs if the file is imported
print("--- Running in Module Mode ---")
print("Calculator logic loaded successfully.")
  • Lines 3–4: Defines a standard function add. This function is available for use regardless of how the file is run.

  • Line 6: Prints the current value of the __name__ variable. This helps us see exactly which mode Python is in (Script vs. Module).

  • Line 8: Checks if the file is being run directly. If true, Python enters the "Script Mode" block.

  • Lines 10–11: These lines execute strictly for testing/running the script directly. They are ignored if this file is imported by another.

  • Lines 12–15: The else block (optional) runs only when the file is imported, allowing us to log that the module has been loaded.

We run this file directly via this command: python calculator.py). The output is:

Debug: The value of __name__ is '__main__'
--- Running in Script Mode ---
10 + 5 = 15

Explanation: Python sets __name__ to "__main__", so the if block executes, running our test code.

Python is often described as a "batteries-included" language. This means it comes pre-installed with a vast collection of useful modules, known as the Standard Library, that handle everything from mathematics and file paths to random number generation and dates.
You do not need to install these modules separately; they are ready to be imported immediately. You can explore the full list of available modules in the official Python Standard Library documentation.

Managing imports and namespaces effectively is the key to scaling Python software. By isolating logic in modules and using imports to connect them, we keep our global namespace clean and our code organized. We also ensure our modules are flexible—usable as standalone tools or as components in a larger system—by checking __name__. With these tools, we are ready to move beyond single-file scripts and start building robust, multi-module applications.