skip to content

Absolute vs Relative Imports

Two ways to name a sibling module inside your own package, and why one breaks the moment you run the file directly. Explaining 'attempted relative import with no known parent package' shows you understand packages.

part ofPythonoverview, primer and where to startread it →
on this pageshow

questions

3

In Python, what is the difference between an absolute import and a relative import?

level: juniorimportance: must knowfreq 62%

answer

  1. Two ways to name another module
  2. One counts from a root, one from here
  3. Leading dots, and what they count from
  4. Package name arithmetic, not directories
  5. PEP 328 deleted the implicit third form

basics

~20 s

An absolute import spells out the full dotted path from an import root, such as from app.core import config. An explicit relative import starts with dots that count outward from the importing module's own package: from . import config.

solid answer

~50 s

An absolute import names a module by its complete dotted path — `import app.core.config` or `from app.core import config` — and the machinery resolves that name against the import roots on `sys.path`, regardless of where the importing file lives. An explicit relative import starts with dots that count outward from the importing module's own package: `from . import config` means "the `config` module beside me", `from ..core import io` means "up one package, then into `core`". The dots are arithmetic on the module's `__package__`, not filesystem navigation and not the working directory. The relative form is legal only in a `from` statement — `import .config` is a `SyntaxError` — and only inside a module that was imported as part of a package. Python 3 removed implicit relative imports (PEP 328), so a bare `import config` inside a package is always absolute.

code

python · 5 lines
python
import json.decoder
from json import decoder

print(json.decoder.__name__)   # json.decoder
print(decoder.__package__)     # json

go deeper

for a junior

Be ready to read a leading dot correctly: one dot is the package the module is in, two dots is the package above it. Know that neither import form looks at the current working directory.

for a middle

Explain that dots are resolved against the importing module's __package__, that the relative form is legal only in a from-statement, and that Python 3 dropped the implicit relative lookup that Python 2 had.

for a senior

Show where each form pays off in real code: relative imports survive a rename or vendoring of the enclosing package, absolute imports are greppable, stable under moves within the tree, and keep working in a module executed on its own.

for a principal

Own the convention rather than the trivia: pick one style per repository, encode it in the lint configuration, and be able to justify it when packages are split across distributions, vendored, or re-namespaced.

### Two ways to spell a module name Every `import` statement hands the import machinery a **module name** to resolve. An *absolute* import gives that name in full: `import app.core.config`, or `from app.core import config`. The machinery walks the dotted name left to right, importing `app`, then `app.core`, then `app.core.config`, checking the module cache in `sys.modules` at each level and otherwise asking the finders that search the interpreter's import roots on `sys.path`. Nothing about where the *importing* file sits on disk takes part in that lookup — an absolute name means the same thing written in any module of any package. An *explicit relative* import gives a name that begins with one or more dots: `from . import config`, `from .config import TIMEOUT`, `from ..core import io`. The dots are not filesystem navigation and have nothing to do with the current working directory. They are arithmetic on the importing module's own **package name**: one dot means "the package I am in", two dots mean "one package up", three dots two up, and so on. The machinery reads that package name from the module's metadata — `__package__`, which for a normally imported module is the same value the module's `__spec__` reports as its parent — joins it with whatever follows the dots, and then resolves the resulting absolute name exactly as if you had typed it out. `importlib.util.resolve_name` performs that one step in isolation, which makes it the easiest way to see the rule: ```python from importlib.util import resolve_name resolve_name(".config", package="app.services") # 'app.services.config' resolve_name("..core.io", package="app.services") # 'app.core.io' ``` ### Why the relative form exists Inside a package, most references are to siblings. Writing `from . import config` instead of `from app.services import config` keeps the package's own top-level name out of every file, so the package can be renamed, nested under another project, or vendored into an application tree without editing its internals. The relative form also states an intent that the absolute form cannot: *this module, the one shipped beside me* — never a same-named module that happens to be installed in the environment. ### The syntax restriction The relative form is legal only in a `from ... import ...` statement. `import .config` is a `SyntaxError`. The reason is mechanical: the plain `import` statement binds the *leftmost* component of the dotted name as a name in the current namespace (`import a.b.c` binds `a`), and a leading dot gives it nothing to bind. So there are exactly two relative shapes: `from . import config`, which imports the sibling *module* and binds the module object, and `from .config import TIMEOUT`, which imports that same module and then pulls one attribute out of it. Both go through identical name resolution; they differ only in what ends up bound in your namespace. ### The third form that no longer exists Python 2 had *implicit* relative imports. Inside `app/services/loader.py`, a bare `import config` searched the module's own package first and only then fell back to a top-level `config`. That rule made it possible to shadow a standard-library name for one package by accident, and made a bare import statement ambiguous to read. PEP 328 removed it. In Python 3, up to and including 3.14, a dot-free name is **always** absolute: `import config` inside `app.services` will never find `app/services/config.py`, and will raise `ModuleNotFoundError` unless a top-level `config` exists. Every relative import in modern Python is therefore explicit, marked by its dots. ### Where a relative import cannot work Because resolution is arithmetic on the package name, a module with no package name has nothing to compute from. That is the case for a file the interpreter runs as the top-level script (on 3.14 its `__package__` and `__spec__` are both `None`), for code typed at the REPL, and for a module imported as a top-level module rather than as part of a package (its `__package__` is the empty string). All three raise `ImportError: attempted relative import with no known parent package`. A separate case is climbing too far: `from ...util import x` inside `app.services` counts up past the top-level package, which raises `ImportError: attempted relative import beyond top-level package`. Neither error consults the filesystem — the machinery never looks at a parent directory to see whether one "could" be a package. ### Reading them in review A useful summary: an absolute import is a coordinate; a relative import is a direction. The coordinate is unambiguous to a reader, greppable across a repository, and unaffected by where the file lives inside its package. The direction is shorter, survives a rename of the enclosing package, and pins the reference to the copy of the package the module ships in — but it changes meaning the moment the file moves to a different depth, and it fails outright in a module that was not imported as part of a package.

  • Why is `import .config` a syntax error when `from . import config` is fine?
    The plain `import` statement binds the leftmost component of the dotted name into the current namespace — `import a.b` binds `a`. A name that starts with a dot has no leftmost component to bind, so the grammar simply does not allow it. The relative form is therefore restricted to `from ... import ...`, where the names being bound are listed explicitly after `import`.
  • What is the difference between `from . import config` and `from .config import TIMEOUT`?
    Both resolve to the same module. The first imports the sibling module and binds the module object as `config`, so you write `config.TIMEOUT`. The second imports that module and then pulls one attribute out of it, binding `TIMEOUT` directly. The choice matters for readability and for circular-import behaviour, not for which module is loaded.
  • Inside `app/services/loader.py`, what does a bare `import config` find in Python 3?
    Only a top-level module or package named `config` on the import path — never the sibling `app/services/config.py`. Python 2's implicit relative lookup would have found the sibling first; PEP 328 removed that, so in Python 3 a dot-free name is always absolute and this raises `ModuleNotFoundError` if no top-level `config` exists.

An absolute import is a street address; a relative import is "two doors down on this street". Both find the same house, but only one still works after the street is renamed, and only one still makes sense once you have moved to another street.

saying these in an interview costs you the question

  • Says relative imports are resolved from the current working directory
  • Thinks a bare import inside a package finds the sibling file first
  • Believes import .config is valid syntax
  • Counts one leading dot as the parent package
  • Claims relative imports search sys.path for the dotted name
  • Says relative imports are deprecated in Python 3

context

open as a page

Why does `from . import config` fail with 'attempted relative import with no known parent package'?

level: middleimportance: should knowfreq 55%

basics

~20 s

Leading dots resolve against the importing module's package name in package. A module that Python did not load as part of a package has no package name, so there is nothing to count outward from and the import machinery raises ImportError.

open as a page

Inside your own importable package, when do relative imports beat absolute ones?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Use relative imports for references inside a package that may be renamed, nested or vendored: they follow the package wherever it goes and always pin the sibling that ships with it. Use absolute imports across packages and in entry-point modules.

open as a page