Python Modules and Packages


Python Modules and Packages Interview with follow-up questions

1. What is a module in Python?

A module is simply a .py file containing Python definitions and statements — its name is the filename without the .py. Importing it makes its functions, classes, and variables available under that module's namespace, which is the basic unit of code reuse and organization.

import math
print(math.sqrt(16))            # access via the module namespace

from math import sqrt, pi       # import specific names

Follow-ups interviewers raise:

  • Modules aren't only .py files: built-in/C-extension modules (sys, math) and packages (directories) are also modules. dir(module) lists its contents.
  • A module's top-level code runs once on first import, then the module object is cached in sys.modules — re-importing doesn't re-execute it.
  • import x vs from x import y: the first keeps the namespace (x.y), the second binds names directly; avoid from x import *, which pollutes the namespace.
  • if __name__ == "__main__": lets a file act as both an importable module and a runnable script — the guarded block only runs when the file is executed directly, not when imported.
  • How imports are found: Python searches sys.path (current dir, PYTHONPATH, then standard locations).
↑ Back to top

Follow-up 1

How do you create a module in Python?

To create a module in Python, you simply create a new .py file and define your functions, classes, or variables in that file. For example, if you want to create a module called 'my_module', you would create a file named 'my_module.py' and define your code in that file.

Follow-up 2

What is the use of the 'import' statement in Python?

The 'import' statement in Python is used to import modules into your current program. It allows you to use the functions, classes, or variables defined in the imported module. For example, if you have a module called 'my_module' and you want to use a function called 'my_function' from that module, you can use the 'import' statement like this: import my_module.

Follow-up 3

Can you explain the 'reload' function in Python?

The 'reload' function in Python is used to reload a previously imported module. It is useful when you have made changes to the module and want to update the changes in your program without restarting the program. The 'reload' function is part of the 'imp' module, and you can use it like this: import imp and then imp.reload(module_name).

Follow-up 4

What is the difference between 'import module' and 'from module import function'?

The 'import module' statement imports the entire module, allowing you to access all the functions, classes, or variables defined in that module using the module name as a prefix. For example, if you import a module called 'my_module' using import my_module, you can access a function called 'my_function' from that module like this: my_module.my_function().

On the other hand, the 'from module import function' statement imports only the specified function from the module, allowing you to use the function directly without using the module name as a prefix. For example, if you import a function called 'my_function' from a module called 'my_module' using from my_module import my_function, you can use the function directly like this: my_function().

2. What is a package in Python?

A package is a directory of modules treated as a single importable namespace, letting you organize a large codebase into a hierarchy (mypkg.subpkg.module). You import from it with dotted paths:

from mypkg.utils import helper
import mypkg.subpkg.module

Follow-ups interviewers expect:

  • __init__.py: a regular package is a directory containing an __init__.py file (which can be empty, or expose a curated public API). Its code runs when the package is first imported.
  • Namespace packages (PEP 420, since 3.3) don't need __init__.py — they let one logical package span multiple directories, which is why an empty __init__.py is no longer strictly required. Use a regular package when you want a defined import-time API; a namespace package when splitting across locations.
  • Relative vs absolute imports: within a package you can write from . import sibling or from .sub import thing; PEP 8 generally prefers absolute imports for clarity.
  • Distribution vs import package: the thing you pip/uv install (the "distribution") can differ in name from the import name, and modern packaging is configured in pyproject.toml.
  • dir(package) and package.__path__ help introspect what's inside.
↑ Back to top

Follow-up 1

How is a package different from a module in Python?

A package is a directory that contains multiple modules and sub-packages, while a module is a single file that contains Python code. Packages provide a way to organize and group related modules together, making it easier to manage and reuse code.

Follow-up 2

How do you create a package in Python?

To create a package in Python, you need to create a directory with a special file called init.py. This file can be empty or can contain initialization code for the package. The directory should also contain the modules and sub-packages that belong to the package.

Follow-up 3

What is the __init__.py file in a Python package?

The init.py file is a special file in a Python package. It is executed when the package is imported and can be used to perform any necessary initialization for the package. It can also be used to define variables, functions, or classes that are available when the package is imported.

Follow-up 4

Can you import modules from a package?

Yes, you can import modules from a package using the import statement. For example, if you have a package called 'my_package' with a module called 'my_module', you can import the module using 'import my_package.my_module'. You can also use the 'from' keyword to import specific objects from a module within a package.

3. Can you explain the concept of namespaces in Python?

A namespace is a mapping from names to objects — effectively a dictionary that tracks which identifier refers to which object. Namespaces keep names from different parts of a program from colliding: math.pi and numpy.pi coexist because each lives in its own module namespace.

Python maintains several kinds, resolved by the LEGB rule — the order in which a name is looked up:

  • Local — names inside the current function.
  • Enclosing — names in any outer (enclosing) function, the basis of closures.
  • Global — names at the top level of the module.
  • Built-in — names always available (len, print, range).
x = "global"
def outer():
    x = "enclosing"
    def inner():
        print(x)      # finds 'enclosing' via LEGB
    inner()

Follow-ups interviewers expect:

  • global and nonlocal: by default assigning to a name creates a local; global x rebinds the module-level name, nonlocal x rebinds the nearest enclosing one. Forgetting this causes the classic UnboundLocalError.
  • Namespaces have different lifetimes: the built-in namespace lasts the whole run, a module's lasts until the program ends, a function's is created on call and destroyed on return.
  • globals(), locals(), and vars() let you inspect them, and dir() lists names in the current scope.
↑ Back to top

Follow-up 1

What is the global namespace in Python?

The global namespace in Python is the namespace that contains names defined at the top level of a module or declared as global inside a function. It is accessible throughout the entire module and can be accessed from any function or class within the module. The global namespace is created when a module is imported or when a script is executed as the main program.

Follow-up 2

What is the local namespace in Python?

The local namespace in Python is the namespace that contains names defined inside a function or method. It is created when the function or method is called and destroyed when the function or method completes execution. The local namespace is separate for each function or method call, which means that each call has its own set of local variables.

Follow-up 3

How does Python handle namespace collisions?

Python handles namespace collisions by following a specific order of name resolution called the 'LEGB' rule:

  • Local: Names defined inside the current function or method.
  • Enclosing: Names defined in the local scope of any enclosing functions or methods, from inner to outer.
  • Global: Names defined at the top level of a module or declared as global inside a function.
  • Built-in: Names that are predefined in the Python built-in namespace.

If a name is not found in the local namespace, Python searches for it in the enclosing namespace, then in the global namespace, and finally in the built-in namespace. If the name is not found in any of these namespaces, a 'NameError' is raised.

Follow-up 4

What is the 'global' keyword in Python?

The 'global' keyword in Python is used to indicate that a variable is a global variable, meaning it should be accessed from the global namespace. When a variable is declared as global inside a function, it can be accessed and modified from both inside and outside the function. Without the 'global' keyword, a variable declared inside a function is considered local to that function and cannot be accessed from outside.

Here's an example:

x = 10

def my_function():
    global x
    x = 20
    print(x)

my_function()  # Output: 20
print(x)  # Output: 20

4. What is the purpose of the PYTHONPATH environment variable?

PYTHONPATH is an environment variable that adds extra directories to Python's module search path. When you import a module, Python looks through sys.path; entries in PYTHONPATH are inserted into that list (after the script's own directory but before the standard library locations), so modules in those directories become importable.

export PYTHONPATH=/home/me/mylibs:$PYTHONPATH
python app.py        # can now `import` modules under /home/me/mylibs

Follow-ups interviewers expect:

  • PYTHONPATH vs sys.path: sys.path is the actual runtime list Python searches; PYTHONPATH is just one source that seeds it (alongside the current dir, stdlib, and site-packages). You can also mutate sys.path in code, though that's a code smell.
  • It's generally a workaround, not best practice. In modern 2026 workflows you avoid fiddling with PYTHONPATH by using a proper package installed into a virtual environment — uv/pip install -e . with a pyproject.toml makes your code importable cleanly, with no env-var hacks.
  • Common pitfall: a PYTHONPATH entry can shadow a stdlib or installed module (e.g. a local random.py), causing confusing import bugs.
  • It's also the wrong tool for picking a Python version — that's what virtual environments and version managers are for.
↑ Back to top

Follow-up 1

How do you set the PYTHONPATH variable?

The PYTHONPATH variable can be set in several ways:

  1. Using the command line: You can set the PYTHONPATH variable using the command line by using the export command in Unix-like systems or the set command in Windows. For example, export PYTHONPATH=/path/to/directory.

  2. Using a script: You can set the PYTHONPATH variable within a Python script by using the os module. For example, import os os.environ['PYTHONPATH'] = '/path/to/directory'.

  3. Using an IDE or text editor: Some IDEs or text editors have settings or preferences where you can specify the PYTHONPATH variable.

It is important to note that the PYTHONPATH variable needs to be set before running a Python script or importing any modules.

Follow-up 2

What happens if a module is not found in the PYTHONPATH?

If a module is not found in the directories specified in the PYTHONPATH variable, Python will raise an ImportError and the import statement will fail. It is important to ensure that the module is installed or located in one of the directories specified in the PYTHONPATH.

Follow-up 3

Can you import a module without adding it to the PYTHONPATH?

Yes, you can import a module without adding it to the PYTHONPATH if the module is located in the same directory as the script that is importing it. Python automatically searches the current directory for modules when importing. However, if the module is located in a different directory, you need to add that directory to the PYTHONPATH or specify the full path to the module in the import statement.

5. What is the 'dir' function in Python?

dir() is a built-in introspection function. Called with an object, it returns a sorted list of that object's attributes and methods; called with no argument, it returns the names in the current local scope.

dir()              # names defined right here
dir(str)           # all attributes/methods of the str type
dir([])            # list methods: append, extend, pop, ...

It's most useful at the REPL or while debugging, to discover "what can I do with this object?" without leaving Python.

Follow-ups interviewers may add:

  • What it actually returns: names including dunder methods (__len__, __iter__). Under the hood it consults the object's __dir__() method, which a class can customize.
  • dir() vs help() vs vars(): dir() lists names; help(obj) shows documented signatures/docstrings; vars(obj) returns the object's __dict__ (its instance attributes only).
  • Related introspection tools: type() for the class, isinstance() for type checks, getattr/hasattr to access names dynamically, and the inspect module for richer introspection (signatures, source).
  • It's a discovery aid, not something you'd rely on in production logic — for stable APIs you'd reference documentation or type hints rather than scraping dir().
↑ Back to top

Follow-up 1

What information does the 'dir' function provide?

The 'dir' function provides a list of names in the current local scope or the attributes and methods of a specified object. It includes the names of variables, modules, functions, classes, and other objects that are defined in the scope or associated with the object.

Follow-up 2

How can you use the 'dir' function with a module?

To use the 'dir' function with a module, you can pass the module object as an argument to the 'dir' function. This will return a list of names that are defined in the module, including variables, functions, classes, and other objects.

Follow-up 3

Can you use the 'dir' function with a package?

Yes, you can use the 'dir' function with a package. When you pass a package object as an argument to the 'dir' function, it will return a list of names that are defined in the package's init.py file, as well as any submodules, functions, classes, and other objects that are defined within the package.

6. How do you manage virtual environments and dependencies in Python (2026)?

A practical question that's almost guaranteed for any applied role. A virtual environment is an isolated per-project Python with its own installed packages, so projects don't clash on dependency versions.

The traditional stack: python -m venv .venv + pip install + a requirements.txt. Activate with source .venv/bin/activate.

The 2026 best-practice answer interviewers increasingly expect: uv (a Rust-based tool from Astral) now consolidates pip, venv, virtualenv, pip-tools, and much of poetry into one fast tool, with a real lockfile (uv.lock) and pyproject.toml as the single source of truth:

uv venv            # create the environment
uv add requests    # add a dependency, update pyproject + lockfile
uv run app.py      # run inside the managed env
uv sync            # reproduce the locked environment exactly

Follow-ups: why lockfiles matter (reproducible builds), pyproject.toml vs the legacy setup.py/requirements.txt, never installing into the system Python, and that uv/pip plus pyproject.toml is the modern packaging direction (PEP 517/518/621). Poetry and Pipenv remain common in existing codebases.

↑ Back to top

Live mock interview

Mock interview: Python Modules and Packages

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.