Content-Length: 185916 | pFad | http://realpython.com/python-init-py/

What Is Python's __init__.py For? – Real Python
What Is Python's __init__.py For?

What Is Python's __init__.py For?

by Charles de Villiers Publication date Jul 09, 2025 Reading time estimate 23m basics python

Python’s special __init__.py file marks a directory as a regular Python package and allows you to import its modules. This file runs automatically the first time you import its containing package. You can use it to initialize package-level variables, define functions or classes, and structure the package’s namespace clearly for users.

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

  • A directory without an __init__.py file becomes a namespace package, which behaves differently from a regular package and may cause slower imports.
  • You can use __init__.py to explicitly define a package’s public API by importing specific modules or functions into the package namespace.
  • The Python convention of using leading underscores helps indicate to users which objects are intended as non-public, although this convention can still be bypassed.
  • Code inside __init__.py runs only once during the first import, even if you run the import statement multiple times.

Understanding how to effectively use __init__.py helps you structure your Python packages in a clear, maintainable way, improving usability and namespace management.

You may have seen files named __init__.py scattered throughout large Python projects and wondered exactly what they do. Or you may have used __init__.py files yourself without a clear idea of why they’re necessary or how to exploit their features. You might also have noticed that your Python code sometimes works even if you forget to add __init__.py to your packages.

So, what is __init__.py for?

Take the Quiz: Test your knowledge with our interactive “What Is Python's __init__.py For?” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

What Is Python's __init__.py For?

Test your understanding of Python's __init__.py files to master how they shape your packages, enhance project structure, and keep your code clean.

In Short: __init__.py Declares a Folder as a Regular Python Package

The special file __init__.py serves as a marker to indicate that its containing directory is a regular package. When people talk about a package in Python, they generally mean a regular package. The __init__.py file is a Python source file, which means that it’s also a module.

If you want to review the terms module and package and how they’re used in Python, then expand the collapsible section below:

In Python, the terms module and package both describe units containing code:

Module Package
Definition A single Python file containing code A directory containing one or more Python modules
Naming The filename without the .py extension The directory name
Contents Functions, classes, and variables Modules and optionally subpackages
Purpose Organizing small projects, simple code reuse Organizing large projects, code reuse
Importing Import directly (import module) Import package or its modules (import package.module)

As you can see, modules and packages both help organize Python code, with modules representing single files and packages representing directories that can group multiple modules.

The existence of __init__.py makes the Python loader identify its containing directory as a regular package. This means you can import the whole directory, specific modules within it, or even individual functions, variables, and classes from those modules using the package name.

You can also import those code objects into other modules, which allows your code to be reused in many flexible ways. You’ll see several examples of this shortly.

As mentioned, __init__.py may be an empty file, in which case it serves only to identify a package.

What Happens When I Add Code to __init__.py?

If __init__.py does contain code, then that code will be executed when the package is first imported.

The code in __init__.py can perform a variety of useful tasks. For example, it can import other modules or packages and define its own functions or data. When a package is imported, all of its own code, along with everything it has itself imported, becomes accessible to the importing module.

You’ll start with a simple example based on a single package. Here’s the setup:

tools_project/
│
└── tools/
    └── __init__.py

So, your project is named tools_project. It has a package named tools. The tools/__init__.py file could have been completely empty, but here it contains a few lines of code:

Python tools_project/tools/__init__.py
__version__ = "1.0.0"
magic_number = 42

With those files in place, start a REPL session in the tools_project directory, and watch what happens when you import and use the tools package:

Python
 1>>> import tools
 2>>> tools.__version__
 3'1.0.0'
 4>>> tools.magic_number
 542

When you executed import tools in line 1, the __init__.py file in the tools directory was implicitly executed. That made the names in tools/__init__.py accessible via the tools prefix. Typically, you might use __init__.py to declare variables that are relevant for the package you’re importing, like the package version or a package-wide constant.

In the rest of this tutorial, you’ll learn more about when, why, and how you might want to use __init__.py.

What Happens if I Don’t Add an __init__.py File?

So far, you’ve been learning about regular packages, but Python also supports packages of another type, known as namespace packages. A namespace package doesn’t have an __init__.py file. You can still import it, provided Python can find it through sys.path. If you don’t add an __init__.py file to a directory, then it will be treated as a namespace package.

Both regular packages and namespace packages define namespaces. The difference lies in the fact that a namespace package isn’t limited to a single directory—it’s the namespace prefix, not the physical directory, that identifies the package contents.

Recall that a namespace is a convention for distinguishing different code objects that may happen to have the same base name, whether that’s the name of a variable, a function, a class, or anything else. By using different prefixes, you can avoid confusion.

For example, in a large body of code, you may very well have two distinct modules named utils. Suppose that one of these contains finance utilities, while the other contains payroll utilities. By placing them in different namespaces—called finance and payroll—you allow Python to distinguish between them, even though they share the same base name. This is because Python treats finance.utils and payroll.utils as entirely separate code objects.

You can easily add a namespace package to your demonstration project. Just add a new, empty directory below tools_project:

tools_project/
│
├── tools/
│   └── __init__.py
│
└── tools_ns/

Recall that both modules and packages must be valid Python identifiers. Hence, your new directory name contains an underscore (_) rather than, say, a hyphen (-).

Now, in the tools_project directory, use the REPL to verify that you can import this package too. Every Python package has a .__path__ attribute. Notice that tools_ns.__path__ looks a little different from tools.__path__:

Python
>>> import tools
>>> tools.__path__
['/tools_project/tools']

>>> import tools_ns
>>> tools_ns.__path__
_NamespacePath(['/tools_project/tools_ns'])

Python finds tools_ns in the current directory, notes that it has no __init__.py within it, and imports it as a namespace package.

Although tools_ns was successfully imported, its .__path__ attribute indicates that it’s a namespace package, not a regular package.

Namespace packages are useful for combining multiple codebases into a unified naming structure. For example, a corporation with a number of development sites might want to combine their various contributions under a single namespace.

For instance, a company called MegaCorp Inc. might use the namespace com.megacorp. Rather than force users to download all the code within their namespace in a single bundle, they can publish smaller, separate libraries that all share the same prefix, such as com.megacorp.finance and com.megacorp.sales.

If MegaCorp publishes these as separate packages, they can be individually maintained, distributed, and used. This approach can be applied to any large software ecosystem or product where logical separation and modularity are key.

Namespace packages certainly have their uses, but they also come with a few caveats. One is that when importing a namespace package, the interpreter must search the whole sys.path to ensure that it has found all the files contributing to the namespace. This results in slower imports. Also, because the full namespace can only be assembled at runtime, any name collisions aren’t detected until then.

Unless you’re sure that you want to build a namespace package, you should opt for regular packages by including an __init__.py file within each package in your project. This will result in more efficient and predictable imports.

The rest of this tutorial will be about regular packages.

What Kind of Code Should I Put in __init__.py?

There’s no requirement to include any executable code inside __init__.py. As you’ve already learned, it’s common to create an empty __init__.py file in a directory, where it serves as a marker to indicate that the directory should be treated as a regular package.

However, __init__.py can also contain functions, classes, data, and absolute or relative import statements.

This can be useful for importing names from submodules to structure a sensible namespace for users of the top-level package. Any names you import into the package become part of its top-level namespace and can then be more conveniently referenced by client code.

__init__.py is a good place to define utility functions, constants, and variables that apply to the whole package.

In summary, you can use the __init__.py file to:

  • Import other modules that are needed by code within __init__.py itself
  • Define variables or functions at the package level
  • Set up configuration or settings for the package
  • Include documentation or define metadata for the package
  • Provide a simplified, coherent namespace for package users

Now that you know what kind of code you may want to include in __init__.py, you may be wondering when Python executes the code in this file.

When Does __init__.py Get Executed?

An __init__.py file is executed the first time its package is imported. This will happen:

  • If you import the package explicitly
  • If you import a module within the package
  • If you import a subpackage of the package, or a module within that subpackage

The __init__.py file starts executing as soon as the interpreter encounters the corresponding import statement. Any nested imports within the file will also be executed as they’re encountered.

How Is __init__.py Used in a Practical Example?

Here’s a concrete example of how to use __init__.py. Suppose you’re starting a new project called media_project, with its own root directory. Below that, a directory named mediatools contains its own __init__.py, defining the mediatools package and namespace. Within that package, you might include subpackages named audio and graphics. Here’s the layout:

media_project/
│
└── mediatools/
    │
    ├── audio/
    │   ├── __init__.py
    │   └── utils.py
    │
    ├── graphics/
    │   ├── __init__.py
    │   └── utils.py
    │
    └── __init__.py

Within media_project, you’ve created the package mediatools, which contains two subpackages: mediatools/audio and mediatools/graphics. The module mediatools/audio/utils.py contains audio utility functions, constant declarations, and the like, while mediatools/graphics/utils.py plays a similar role for the graphics part of your project.

Notice that each package has its own __init__.py, so these are regular packages. For the purposes of this example, you’ll populate the mediatools/audio/utils.py module as follows:

Python mediatools/audio/utils.py
 1print(f"Importing {__name__}")
 2
 3def wobbulate():
 4    print("Wibble wobble")
 5
 6def enhance():
 7    print("Enhancing audio")
 8
 9_secret_password = "Alakazam!"
10__top_secret_password = "!mazakalA"

You’ll be adding a line like line 1 to each Python file from now on. That way, you’ll see exactly when that package or module is imported.

The two variables _secret_password and __top_secret_password in lines 9 and 10 are called non-public variables. They’re not meant to be used outside their own module. You’ll soon explore what this means in practice.

You’ll also add some code to mediatools/graphics/utils.py:

Python mediatools/graphics/utils.py
print(f"Importing {__name__}")

def solarize():
    print("Solarizing")

def enhance():
    print("Enhancing graphics")

indiana_pi = 3.2

So the audio and graphics packages each contain a module named utils.py, and each of those contains a debugging call to print() at the top that’ll display the module’s name, some function definitions, and variable declarations.

For example, the audio.utils module contains the function wobbulate(). If you had a main program in the media_project directory, it could arrange to call that function in various ways, such as by importing the module with its full namespace prefix:

Python
import mediatools.audio.utils

mediatools.audio.utils.wobbulate()

Or by importing just the module name:

Python
from mediatools.audio import utils

utils.wobbulate()

Or by importing the function name with no prefix at all:

Python
from mediatools.audio.utils import wobbulate

wobbulate()

Next, add your demonstration call to print() to mediatools/__init__.py, so you’ll know when it’s executed:

Python mediatools/__init__.py
print(f"Importing {__name__}")

Add the same line to graphics/__init__.py:

Python mediatools/graphics/__init__.py
print(f"Importing {__name__}")

And also to audio/__init__.py:

Python mediatools/audio/__init__.py
print(f"Importing {__name__}")

With these debugging calls to print() set up, you’ll be able to observe when the code in each __init__.py is executed. This happens when its package is imported. You can see this in action by starting a new REPL session:

Python
>>> from mediatools.audio import utils as audio_utils
Importing mediatools
Importing mediatools.audio
Importing mediatools.audio.utils

>>> from mediatools.graphics import utils as graphics_utils
Importing mediatools.graphics
Importing mediatools.graphics.utils

>>> audio_utils.wobbulate()
Wibble wobble

>>> graphics_utils.solarize()
Solarizing

>>> graphics_utils.indiana_pi
3.2

In this code, you’ve chosen the prefix audio_utils to indicate that a function comes from the utils module within the audio package. Similarly, the prefix graphics_utils identifies a function from the utils module within the graphics package.

Even though your import statements explicitly mentioned only the two utils modules, your calls to print() show that the code in mediatools.__init__.py, mediatools.audio.__init__.py, and mediatools.graphics.__init__.py was implicitly executed. So, the first three lines printed to your console after importing the utils module from mediatools.audio come from the following files:

  • mediatools/__init__.py prints Importing mediatools
  • mediatools/audio/__init__.py prints Importing mediatools.audio
  • mediatools/audio/utils.py prints Importing mediatools.audio.utils

Notice that the code in mediatools.__init__.py was executed only once, even though mediatools is a parent package of both the imported modules. You’ll see why this happens in a moment.

You’ve confirmed that the code in the __init__.py file runs automatically when its package, or anything within it, is imported. While this code can do nearly anything, its usual purpose is to initialize the package and define any attributes, functions, or data that are global to the package. It may also perform imports, either for its own internal use or to expose the imported names to client code.

The Python interpreter maintains a cache of imported modules, so you can’t import the same module twice using a normal import statement. Python silently skips the second import statement, and the contents of __init__.py won’t be re-executed. If you import two subpackages from the same parent package, then the parent and each subpackage will be imported exactly once.

See what happens when you start a new REPL and attempt to load audio.utils twice:

Python
>>> from mediatools.audio import utils
Importing mediatools
Importing mediatools.audio
Importing mediatools.audio.utils

>>> from mediatools.audio import utils as more_utils

>>> utils.enhance()
Enhancing audio

>>> more_utils.enhance()
Enhancing audio

The second import statement didn’t execute any imports—it only aliased the previous import utils to the name more_utils.

Before continuing, you can remove the debugging calls to print() from all your files to avoid cluttering your output in the following sections.

Can I Use __init__.py to Control What’s Exported From a Package?

The short answer is this: not really. Python does have mechanisms and conventions to control exports, but they’re easily circumvented. One such convention concerns names that start with one or more leading underscores. The convention dictates that external code shouldn’t reference these names. By using these “underscore names,” the package developer can help the package user to understand what should and shouldn’t be included in the public API.

In practice, it’s the package developer’s responsibility to use this convention to indicate the things that aren’t intended for export, but it’s up to the package user to respect the convention. It can easily be ignored, as you can see in the following code snippet:

Python
>>> import mediatools.audio.utils

>>> mediatools.audio.utils._secret_password
'Alakazam!'

>>> mediatools.audio.utils.__top_secret_password
'!mazakalA'

Python is happy to import and print the values of these supposedly non-public variables.

The import * syntax does respect the underscore convention. You can use this syntax to import the contents of the module mediatools/audio/utils.py in a new REPL session:

Python
>>> from mediatools.audio.utils import *

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

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

With this syntax, most of the names inside mediatools.audio.utils become available to your program without the prefix, but the underscore variables aren’t imported.

To see this issue in practice, start a new REPL session in the media_project directory and use import * for your submodules:

Python
>>> from mediatools.audio.utils import *

>>> from mediatools.graphics.utils import *

>>> wobbulate()
Wibble wobble

>>> indiana_pi
3.2

>>> solarize()
Solarizing

>>> enhance()
Enhancing graphics

The interpreter can no longer distinguish between the two versions of enhance(). In fact, the second import * imported the name enhance, without a namespace prefix, from graphics.utils. This clobbered the enhance imported from audio.utils, resulting in unexpected behavior.

You can demonstrate the use of __all__ by adding a line to mediatools/graphics/utils.py:

Python mediatools/graphics/utils.py
# ...

__all__ = ["solarize", "enhance"]

With this addition, import * will import only the two named functions, and the variable indiana_pi is no longer visible:

Python
>>> from mediatools.graphics.utils import *

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

Although you can’t prohibit your package’s users from importing symbols in a way you didn’t intend, you can structure your namespace in a way that helps them understand how the code is meant to work.

For example, instead of expecting a developer to dig into the details of the two utils packages to decide what to import and how to import it, you can add a couple of lines to mediatools/__init__.py to make things a little more self-explanatory:

Python mediatools/__init__.py
from .audio.utils import enhance as audio_enhance
from .audio.utils import wobbulate
from .graphics.utils import enhance as graphics_enhance
from .graphics.utils import solarize

Here you’ve used some relative imports to simplify the developer’s view of the mediatools package. This conforms to the software engineering principle of information hiding: the package user doesn’t need to see the gory details of mediatools and its subpackages. They simply need an interface definition, as shown above, to help them to use the package at a high level.

Now the user code can take advantage of the new interface:

Python
>>> from mediatools import (
...     wobbulate,
...     solarize,
...     audio_enhance,
...     graphics_enhance
... )

>>> wobbulate()
Wibble wobble

>>> solarize()
Solarizing

>>> audio_enhance()
Enhancing audio

>>> graphics_enhance()
Enhancing graphics

Using this interface, client code is better decoupled from the internals of the mediatools packages. This makes the client code easier to write and easier to maintain. Notice that your single import statement caused a chain reaction of imports: first the parent package, then each subpackage with its module.

Sometimes, you want to import packages into __init__.py for its internal use, but to avoid polluting the namespace, you don’t want to expose those names to the users of your package.

Here’s a schematic __init__.py file that does this for an imaginary application:

Python
from datetime import date as _date
from math import gcd as _gcd
from some.thirdparty.dateutils import lunar_month as _lunar_month

def fancy_date_calculator() -> str:
    today = _date.today
    # Some fancy calculations using _gcd and _lunar_month
    ...
    return "Calculated result"

When user code imports this package, it will see only one public function: fancy_date_calculator(). All the other symbols will be prefixed with underscores, making it clear that they’re not intended for public use.

Is a Top-Level __init__.py Different From One in a Subpackage?

The __init__.py in the folder at the top level of an import tree often contains some extra metadata about the library as a whole. This can include its version number, the URL of its project site, and contact information for the developers. Otherwise, it behaves in the same way as any other __init__.py.

For example, say you want to add a package version number. A good spot for that would be inside of mediatools/__init__.py:

Python mediatools/__init__.py
__version__ = "0.1.0"

# ...

You’d likely keep the other __init__.py files empty, although nothing prevents you from recording version information for your subpackages there as well.

Conclusion

In this tutorial, you’ve learned about the special role of Python’s __init__.py file in defining regular packages and distinguishing them from namespace packages.

You’ve explored how Python executes code within __init__.py upon package import, how to use it to structure namespaces and APIs, and how to handle imports effectively. Additionally, you’ve learned about Python’s underscore naming conventions for non-public symbols and how the __all__ variable can help explicitly define a module’s public interface.

Effectively using __init__.py can help you create well-organized, maintainable, and intuitive packages. By structuring your packages properly, you improve readability, encourage reusability, and simplify package-level initialization.

In this tutorial, you’ve learned how to:

  • Mark directories as regular packages using __init__.py
  • Define a clear, explicit public API using imports in __init__.py
  • Use underscore prefixes to indicate non-public symbols
  • Control what’s imported with the __all__ variable
  • Understand when and how Python executes code in __init__.py

Next time you create an __init__.py file, you’ll understand exactly how it fits into your project and be ready to add code that structures your namespaces, initializes data, or stores metadata.

Frequently Asked Questions

Now that you have some experience with __init__.py in Python, 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.

An empty __init__.py file indicates to Python that the directory is a regular package, allowing you to import modules from it.

Without __init__.py, Python recognizes the directory as a namespace package instead of a regular package. Namespace packages can span multiple directories, but their imports are usually slower.

You can’t strictly enforce what’s imported, but you can guide users by explicitly defining a public API within your __init__.py file. Python conventions like leading underscores and the __all__ variable help users understand your intended use.

Python executes the code in __init__.py the first time you import the package or any of its modules. Python runs this code only once, caching the package for subsequent imports.

Take the Quiz: Test your knowledge with our interactive “What Is Python's __init__.py For?” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

What Is Python's __init__.py For?

Test your understanding of Python's __init__.py files to master how they shape your packages, enhance project structure, and keep your code clean.

🐍 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 Charles de Villiers

Charles teaches Physics and Math. When he isn't teaching or coding, he spends way too much time playing online chess.

» More about Charles

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!

Keep Learning

Related Topics: basics python









ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://realpython.com/python-init-py/

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy