Python
init.py
Python packages
Python 3.3
programming

Is __init__.py not required for packages in Python 3.3

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Starting with Python 3.3, a directory can sometimes act as a package even if it does not contain __init__.py. That change came from implicit namespace packages. But "not required" does not mean "never useful" or "always optional." In many projects, __init__.py is still the right file to include.

What Changed in Python 3.3

Before Python 3.3, the normal rule was simple: if you wanted a directory to be importable as a package, that directory needed an __init__.py file.

Python 3.3 introduced implicit namespace packages. With that feature, Python can treat matching directory names on the import path as parts of one logical package even when they do not contain __init__.py.

That means this can now work in some cases:

text
project/
    mypkg/
        module_a.py

And then:

python
import mypkg.module_a

The absence of __init__.py no longer automatically blocks the import.

What a Namespace Package Is

A namespace package lets multiple directories contribute to the same package name. This is useful when a logical package is spread across different distributions or locations on sys.path.

For example, imagine two different installed distributions both contributing under the same top-level package name:

text
1path_one/
2    company/
3        analytics/
4            reports.py
5
6path_two/
7    company/
8        auth/
9            login.py

Python can treat both company directories as parts of one namespace package, allowing imports like:

python
import company.analytics.reports
import company.auth.login

That is the main motivation for the feature.

When __init__.py Is Still Useful

Even though it is no longer always required, __init__.py still matters when you want package-level behavior.

Typical reasons to keep it include:

  • marking the package explicitly for clarity
  • running package initialization code
  • defining __all__
  • re-exporting selected names
  • avoiding accidental namespace-package behavior

Example:

python
1# mypkg/__init__.py
2from .api import Client
3
4__all__ = ["Client"]

Without __init__.py, there is no package file in which to place that logic.

A Simple Example

Suppose you have:

text
demo/
    tools/
        formatter.py

And formatter.py contains:

python
def format_name(name):
    return name.strip().title()

In Python 3.3 and later, you may be able to import it without tools/__init__.py:

python
from tools.formatter import format_name

print(format_name("  ada lovelace  "))

That works because tools can be treated as an implicit namespace package.

Why Many Projects Still Include It

In real projects, adding __init__.py often makes intent clearer and avoids surprises in tooling, packaging, and imports. Namespace packages are powerful, but they are also more implicit.

If your package is a normal single-directory package inside one application, __init__.py is still a sensible default. It makes the package explicit and gives you a place for package-level exports.

In other words:

  • namespace packages are useful when you need them
  • ordinary packages with __init__.py are still perfectly normal

One Important Distinction

"Not required" is not the same as "ignored." If __init__.py exists, Python treats the directory as a regular package, not an implicit namespace package.

That distinction matters because regular packages and namespace packages differ in behavior and use cases. Namespace packages are designed for composition across multiple paths. Regular packages are a single defined package with an actual package module file.

Practical Guidance

Use implicit namespace packages when:

  • multiple separately distributed components should share a package namespace
  • you intentionally want package composition across multiple locations

Use __init__.py when:

  • the package is ordinary and self-contained
  • you want explicit package structure
  • you need package initialization or exports
  • you want to reduce ambiguity for humans and tools

For most application codebases, including __init__.py is still the simpler choice unless you have a reason not to.

Common Pitfalls

  • Interpreting Python 3.3's change as "never use __init__.py again." The file is still useful and common.
  • Forgetting that namespace packages exist for a specific composition use case, not as a universal replacement.
  • Expecting package-level initialization to work without __init__.py. There is no file to execute in that case.
  • Mixing regular-package and namespace-package assumptions in the same codebase without understanding the difference.
  • Blaming import issues on Python alone when packaging layout or sys.path setup is the real cause.

Summary

  • Python 3.3 introduced implicit namespace packages, so __init__.py is no longer always required.
  • A directory without __init__.py can still be importable as a package in the right context.
  • '__init__.py remains useful for initialization, exports, and explicit package structure.'
  • Namespace packages are most useful when a package spans multiple directories or distributions.
  • For ordinary self-contained packages, including __init__.py is still often the clearest choice.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.