What is __init__.py?
A Google search leads to stackoverflow which links to the python documentation. The gist is that __init__.py is used to indicate that a directory is a python package. (A quick note about packages vs. modules from the python docs: "From a file system perspective, packages are directories and modules are files."). If we want a folder to be considered a python package, we need to include a __init__.py file. But what should we put in it?
What to put in __init__.py
There is a range of options of what to put in an __init__.py file. The most minimal thing to do is to leave the __init__.py file totally empty. Moving slightly away from this, while still keeping things simple, you can use an __init__.py only for determining import order. Another step up on the spectrum, you can use the __init__.py file to define the API of the package. And the final step is you can actually just define your entire package in the __init__.py .
I will dig into the pro’s an cons of each of these 4 approaches and give examples of them in the wild in the rest of the post.
#1 Leave it Empty
This approach is the simplest to communicate and the simplest to enforce. There is no gray area about not including anything in an __init__.py . The file will serve it’s purpose of indicating the folder should be considered a python package, and nothing else.
One disadvantage of this approach is that it fails to take advantage of the special status of the __init__.py file. The code in the file will be executed in the course of importing any of the package’s submodules. For instance, if we have a project with the following directory structure:
And we want to import the "a" module, the statement from foo import a looks in the foo directory, sees the __init__.py . It knows to treat foo as a package, and it executes it’s __init__.py , then looks for how to import a . (You can verify this behavior by recreating this directory structure and putting print statements in the files. If you do from foo import c , you’ll get an ImportError , but not after the print statement in foo/__init__.py executes. If you are interested in digging into the python source code, the code for importlib is available on github. Also the spec for the generic Importer Protocol is in PEP-302). By leaving our __init__.py file blank, we miss out on the opportunity to leverage this.
Another disadvantage is related to namespaces. In the directory structure listed above, importing foo anywhere will be useless. In order to access any of our actual code, we have to import sub modules. In addition to making import statements longer, naming things is hard. It is unfortunate to come up with a great name for a package or a sub-package and then also need to come up with good names for sub-modules since that is what you will end up referring to.
An example in the python source of this approach being used is in urllib.
#2 Enforce Import Order
This is what mssaxm over at axialcorps.com recommends in a post titled 5 Simple Rules For Building Great Python Packages. This approach takes advantage of the special behavior of __init__.py while still keeping the file simple. This approach really shines if your sub-modules have some static initialization. For example, let’s say a.py writes a config file when it is imported, and b.py reads from that file. We could have our __init__.py ensure that a.py is always run before b.py by having it’s contents be:
Then when we run import foo.b , it is guaranteed that a.py would be executed before b.py . (This dependency example is a bit contrived; I do not mean to suggest that sub-modules should make a habit of writing out files on import.)
Since this approach does not allow non-import code in the __init__.py , it seems to suffer from the namespace issue described in #1 above. However, this can be circumvented by importing member from individual packages. For instance, if we had a my_func that we wanted to be able to access as import foo; foo.my_func() , we could put my_func in a.py and then have our __init__.py be from a import my_func .
An example of this approach being used is the fsq package described by in the post I mentioned above.
#3 Define API
In this approach, the __init__.py file houses the most visible functionality for the package. It pieces together the functionality from the sub-modules. This approach has the advantage of providing a good starting point to look into a package, and makes it clear what the top level functionality is.
The disadvantage is that your __init__.py file is more complicated. The more complicated it gets, and the more deeply nested your package structure gets, the greater the risk of this causing problems. Remember that importing a deeply nested package executes the __init__.py of every parent package.
Another disadvantage of this approach is that it can be difficult to decide what deserves to be in the __init__.py vs. in a sub-module. As the file gets bigger and more complex, a call will need to be made about when to pull things out.
An example of this approach in python library code is in the json module. The __init__.py file exposes the dump , dumps and loads functions which rely on functionality defined in sub-modules.
#4 The Whole Package
The final approach is to put the entire package in the __init__.py file. This can work well for small packages. It avoids needing to come up with a bunch of new names.
As the package gets larger however, a single file package can become unwieldy.
An example of this approach is collections module. (Although, technically it does have one sub-module.)
Packages and Packaging
Before we get started on making your own package – let’s remind ourselves about packages and modules, and importing….
A python “module” is a single namespace, with a collection of values:
-
functions
-
constants
-
class definitions
-
really any old value.
A module usually corresponds to a single file: something.py
A “package” is essentially a module, except it can have other modules (and indeed other packages) inside it.
A package usually corresponds to a directory with a file in it called __init__.py and any number of python files or other package directories:
The __init__.py can be totally empty – or it can have arbitrary python code in it. The code will be run when the package is imported – just like a module.
Modules inside packages are not automatically imported. So, with the above structure:
will run the code in a_package/__init__.py . Any names defined in the __init__.py will be available in:
will not exist. To get submodules, you need to explicitly import them like so:
More on Importing
You usually import a module like this:
or a few names from a package:
You also can optionally rename stuff as you import it:
This is a common pattern for using large packages (maybe with long names) and not having to type a lot.
import *
Means: “import all the names in the module, “something”.
You really don’t want to do that! It is an old pattern that is now an anti-pattern.
But if you do encounter it, it doesn’t actually import all the names – it imports the ones defined in the module’s __all__ variable.
__all__ is a list of names that you want import * to import. So the module author can control it, and not accidentally override builtins or bring a lot of extraneous names into your namespace.
Don’t Use «import *«
Relative imports
Relative imports were added with PEP 328:
The final version is described here:
This gets confusing! There is a good discussion on Stack Overflow here:
Relative imports allow you to refer to other modules relative to where the existing module is in the package hierarchy, rather than in the entire python module namespace. For instance, with the following package structure:
You can do (in moduleX.py ):
Similarly to command line shells:
“.” means “the current package”
“..” means “the package above this one”
Note that you have to use the from form of import when using relative imports.
(That’s current package, not current module!)
you can only use relative imports from within a package
you can not use relative imports from the interpreter
you can not use relative imports from a top-level script (if __name__ is set to __main__ . So the same python file with relative imports can work if it’s imported, but not if it’s run as a script)
The alternative is to always use absolute imports:
Advantages of Relative Imports:
Package does not have to be installed
You can move things around, and not much has to change
Advantages of Absolute Imports
explicit is better than implicit
imports are the same regardless of where you put the package
imports are the same in package code, command line, tests, scripts, etc.
There is debate about which is the “one way to do it” – a bit unpythonic, but you’ll need to make your own decision.
sys.modules
sys.modules is simply a dictionary that stores all the already imported modules. The keys are the module names, and the values are the module objects themselves.
Remember that everything in Python is an object – including modules. So they can be stored in lists and dict, assigned names, even passed to functions – just like any other object. They are not often used that way, but they can be.
You can access the module through the sys.modules dict:
Which is the same as:
So, more or less, when you import a module, the interpreter:
Looks to see if the module is already in sys.modules .
If it is, it binds a name to the existing module in the current module’s namespace.
-
A module object is created
-
The code in the file is run
-
The module is added to sys.modules
-
The module is added to the current namespace.
Implications of module import process:
The code in a module only runs once per program run.
Importing a module again is cheap and fast.
Every place your code imports a module it gets the same object — You can use this to share “global” state where you want to.
If you change the code in a module while the program is running – the change will not show up, even if re-imported.
That’s what importlib.reload() is for.
The module search path
The interpreter keeps a list ( sys.path ) of all the places that it looks for modules or packages when you do an import:
you can manipulate that list to add or remove paths to let python find modules in a new place.
Every module has a __file__ name that points to the path it lives in. This lets you add paths relative to where you are, etc.
Note
It’s usually better to use setuptools’ “develop” mode (or pip install -e ) instead of messing with sys.path – see below.
One “gotcha” in Python is “name shadowing”. The interpreter automatically adds the “current working directory” to sys.path . This means you can start the interpreter and just import something to work with your code. But if you happen to have a python file, or package, in your current working directory that’s the same as an installed package, then it will get imported instead, which can lead to some odd errors. If you are getting confusing errors on import – check for python modules in your current working directory that may match an installed package!
Reloading
Once loaded, a module stays loaded.
If you import it again (usually in another module) it will simply use the version already there – rather than re-running the code.
And you can access all the already loaded modules from sys.modules . sys.modules is a dict with the module names as the keys, and the module objects as the values
There’s no reason too – but you could import an already imported module like so:
Python Distributions
So far, we’ve used the Python from python.org. It works great, and supports a lots of packages via pip.
But there are also a few “curated” distributions. These provide python and a package management system for hard-to-build packages.
Widely used by the scipy community:
(lots of hard to build stuff that needs to work together…)
-
Anaconda (https://store.continuum.io/cshop/anaconda/) and miniconda
-
ActivePython (http://www.activestate.com/activepython)
Conda has seen a LOT of growth in the last few years – it’s based on the open-source conda packaging system, and provides both a commercial curated set of packages, and a community-developed collection of packages known as conda-forge:
If you are doing data science or scientific development – I recommend you take a look at Anaconda, conda and conda-forge.
Installing Packages
Every Python installation has its own stdlib and site-packages folder.
site-packages is the default place for third-party packages.
From source
With the system installer (apt-get, yum, etc…)
From binaries
Binary wheels – (More and more of those available)
pip should find appropriate binary wheels if they are there.
A bit of history:
In the beginning, there was the distutils :
But distutils is missing some key features:
And then came PyPi
And then came setuptools (with easy_install)
But that wasn’t well maintained…
Then there was distribute/pip
Which has now been merged back into setuptools
Now it’s pretty stable: pip+setuptools+wheel: use them.
warning – setuptools still provides easy_install, but it has mostly been deprecated, so you really want to use pip. And sometimes setuptools will invoke it for you under the hood by accident 🙁
Installing Packages
Actually, it’s still a bit of a mess
But getting better, and the mess is almost cleaned up.
Current State of Packaging
To build packages: setuptools
-
https://setuptools.readthedocs.io/en/latest/
setuptools provides extensions to the build-in distutils:
But there are a couple of those extensions that you really do need, so most folks use setuptools for everything. In fact, pip itself requires setuptools.
To install packages: pip
-
https://pip.pypa.io/en/latest/installing.html
For binary packages: wheels
-
http://www.python.org/dev/peps/pep-0427/
(installable by pip)
Compiled Packages
Biggest issue is with compiled extensions:
-
(C/C++, Fortran, etc.)
-
You need the right compiler set up
-
Here’s where it gets really ugly
-
Particularly on Windows
Linux
Is there a system package?
-
use it (apt-get install the_package)
Try pip install : it may just work!
Install the dependencies, build from source:
(may need “something-devel” packages)
Windows
A lot of packages have Windows wheels now.
-
Often installable with pip (pip will install a wheel for you if it exists)
-
Usually for python.org builds
-
Excellent source: http://www.lfd.uci.edu/
But if no binaries:
-
Hope the dependencies are available!
-
Set up the compiler
Each version of Python requires a particular version of the MS Compiler:
You can get the one for recent Pythons here.
Lots of Python versions:
-
Apple’s built-in (different for each version of OS)
-
python.org builds
-
32+64 bit Intel (and even PPC still kicking around)
-
Macports
-
Homebrew
Binary wheels are pretty much compatible between them – yeah!
If you have to build it yourself
Xcode compiler (the right version)
-
Version 3.* for 32 bit PPC+Intel
-
Version > 4.* for 32+64 bit Intel
(make sure to get the SDKs for older versions)
If extra dependencies:
-
macports or homebrew often easiest way to build them
Final Recommendations
First try: pip install
If that doesn’t work:
Read the docs of the package you want to install
Do what they say
virtualenv
virtualenv is a tool to create isolated Python environments.
Very useful for developing multiple apps
Or deploying more than one app on one system
You can find some additional notes here: Working with Virtualenv
NOTE: conda also provides a similar isolated environment system.
Building Your Own Package
The term “package” is overloaded in Python. As defined above, it means a collection of python modules. But it often is used to refer to not just the modules themselves, but the whole collection, with documentation and tests, bundled up and installable on other systems.
Here are the very basics of what you need to know to make your own package.
Why Build a Package?
There are a bunch of nifty tools that help you build, install and distribute packages.
Using a well structured, standard layout for your package makes it easy to use those tools.
Even if you never want to give anyone else your code, a well structured package eases development.
What is a Package?
A collection of modules
… and the documentation
… and any top-level scripts
… and any data files required
… and a way to build and install it…
Python packaging tools:
Getting klunky, hard to extend, maybe destined for deprecation…
You really need to use setuptools these days, which fortunatly has a similar API:
pip : for installing packages
wheel : for binary distributions
These are pretty much the standard now – very well maintained by:
“The Python Packaging Authority” – PaPA
This all continues to change quickly, see that site for up to date information.
Where do I go to figure this out?
This is a really good guide:
Python Packaging User Guide:
and a more detailed tutorial:
Follow one of them
There is a sample project here:
(this has all the complexity you might need…)
You can use this as a template for your own packages.
Here is an opinionated update – a little more fancy, but some good ideas:
Rather than doing it by hand, you can use the nifty “cookie cutter” project:
And there are a few templates that can be used with that.
The core template written by the author:
And one written by the author of the opinionated blog post above:
Either are great starting points.
One confusion for folks new to this is that a LOT of the documentation (and tools) around packaging for Python assumes that you are writing a package that is generally useful, and you want to share it with others on PyPi. That is partly because all the people developing the tools and writing about them are doing just that. It’s also harder to distribute a package properly than to simply make one for internal use, so more tools and docs are needed. But it is still useful to make a package of your code if you aren’t going to distribute it, but you don’t need to do everything that is recommended. See: A Package Just for You for a really simple way to do the basics.
Basic Package Structure:
CHANGES.txt : log of changes with each release
LICENSE.txt : text of the license you choose (do choose one!)
MANIFEST.in : description of what non-code files to include
README.txt : description of the package – should be written in ReST (for PyPi):
(those are all “metadata” critical if you are distributing to the world – not so much for your own use)
setup.py : distutils / setuptools script for building/installing the package.
bin/ : This is where you put top-level scripts
( some folks use scripts )
docs/ : the documentation
package_name/ : The main package – this is where the code goes.
test/ : your unit tests. Options here:
Put it inside the package – this results in the tests getting isntalled with the package, so they can be run after installation, with:
Or, if you have a lot of tests, and do not want the entire set installed with the package, you can keep it at the top level.
The setup.py File
Your setup.py file is what describes your package, and tells the setuptools how to package, build, and install it
It is python code, so you can add anything custom you need to it.
But in the simple case, it is essentially declarative.
An example:
setup.cfg
NOTE: this is usually a pretty advanced option – simple packages don’t need this.
setup.cfg provides a way to give the end user some ability to customize the install
It’s an ini style file:
simple to read and write.
command is one of the distutils commands (e.g. build, install)
option is one of the options that command supports.
Note that an option spelled —foo-bar on the command-line is spelled f«foo_bar« in configuration files.
Running setup.py
With a setup.py script defined, setuptools, along with pip, can do a lot:
builds a source distribution (a tar archive of all the files needed to build and install the package):
(you need the wheel package for this to work: pip install wheel )
build from source:
(the dot means “this directory” – pip will look in the current dir for a setup.py file)
install in “develop” or “editable” mode:
setuptools can be used by itself to build and install packages. But over the years, pip has evolved to a more “modern” way of doing things. When you install from source with pip – it is using setuptools to do the work, but it changes things around, and installs things in a more modern, up to date, and compatible way. For much use, you won’t notice the difference, but it setuptools still has some old crufty ways of doing things, so it’s better to use pip as a front end as much as possible.
setuptools
setuptools is an extension to distutils that provides a number of extensions:
superset of the distutils setup
This buys you a bunch of additional functionality:
-
auto-finding packages
-
better script installation
-
resource (non-code files) management
-
develop mode
-
a LOT more
In fact, virtually all python packages use setuptools these days, and there is currently discussion of deprecating distutils, and making setuptools “official”. So you really want to use it.
wheels
Wheels are a binary format for packages.
Pretty simple, essentially a zip archive of all the stuff that gets installed, i.e. put in site-packages .
Can be just pure python or binary with compiled extensions
Compatible with virtualenv.
Building a wheel:
pip install packagename will find wheels for Windows and OS-X and “manylinux”
pip install —no-use-wheel avoids that, and forces a source install.
manylinux
There are a lot of Linux distributions out there. So for a long time, there were not easily available binary wheels for Linux – how could you define a standard with all the Linux distros out there?
Enter “manylinux” – no one thinks you can support all Linux distros, but it was found that you could support many of the common ones, by building on an older version and restricting system libraries. This approach worked well for Canopy and conda, so PyPi adopted a similar strategy with manylinux:
So there are now binary wheels for Linux on PyPi.
The core scipy stack is a great example – you can now pip install numpy on all three systems easily with pip.
The Python package index:
You’ve all used this – pip install searches it.
To upload your package to PyPi:
NOTE: only do this if you really want to share your package with the world!
Under Development
Develop mode (or “editable install”) is really really nice:
(the e stands for “editable” – it is the same thing)
It puts links into the python installation to your code, so that your package is installed, but any changes will immediately take effect.
This way all your test code, and client code, etc, can all import your package the usual way.
No sys.path hacking
Good idea to use it for anything more than a single file project.
Running tests
It can be a good idea to set up your tests to be run from setup.py
So that you (or your users) can:
Note: there is debate about whether this is a good idea. But if you want to:
To do this, you need to add a test_suite stanza in setup.py.
pytest
And create an alias into setup.cfg file:
This may not be required, as pytest will also let you run the tests installed with a package with:
unittest
Handling the version number:
One key rule in software (and ANY computer use!):
Never put the same information in more than one place!
With a python package, you want:
To return the version string – something like:
Using __version__ is not a requirement, but it is a very commonly used convention – use it!
But you also need to specify it in the setup.py :
My solution:
Put the version in the package __init__
In the setup.py, you could import the package to get the version number … but it’s not a safe practice to import your package when installing it (or building it, or…)
So: read the __version__ string yourself with code like:
Alternative:
You can have a script that automatically updates the version number in whatever places it needs to. For instance:
Though I think it’s better to have the version set in the code itself.
Semantic Versioning
Another note on version numbers.
The software development world (at least the open-source one…) has established a standard for what version numbers mean, known as semantic versioning. This is helpful to users, as they can know what to expect they upgrade.
In short, with a x.y.z version number:
x is the Major Version – it could mean changes in API, major features, etc.
-
Likely to to be incompatible with previous versions
y is the Minor Version – added features, etc, that are backwards compatible.
z is the “Patch” Version – bug fixes, etc. – should be fully compatible.
Read all about it:
Tools to help:
Dealing with data files
Oftentimes a package will require some files that are not Python code. In that case, you need to make sure the files are included with the package some how.
There are a few ways to do this:
The simplest option: package_data
I personally like the simplest one with the least magic:
This is a dict with the keys being the package(s) you want to add data files to. This is required, as a single setup command can install more than one package. The value(s) is a list of filenames, relative to the package — note that in the above example, the “pkg_name” is not part of the path to the file.
WARNING: For some reason, setuptools does not give you an error or warning if it can’t find the files you specify – which is a real shame — makes it harder to debug.
Then you’ll get the data file included in the package in the same place relative to your code regardless of how (or whether) it is installed.
Debugging package building can be kind of tricky: if you install the package, and it doesn’t work, what went wrong. One approach that can help is to “build” the package, separately from installing it. setuptools provides a build command: python setup.py build that does just that. It will create a build directory, and in there, a lib dir. In there is what will actually get installed – your “built” package. So you can look there and see if your data files are getting included, and everything else about the package.
Now you’ll need to write your code to find that data file. You can do that by using the __file__ module attribute – then the location of the data file will be relative to the __file__ that your code is in. A little massaging with a pathlib.Path should do it. Putting the path to the data directory in the package’s __init__.py provides a way for the rest of your code to find it.
Now you can get that dir anywhere else in your code:
More complex option: data_files
Using the data_files setup option lets you put data files outside your package. They will get installed into the path of sys.prefix , so you can find them in your code with a path relative to sys.prefix .
However, this means that the location of the files is different depending on whether the code is properly installed or not. And develop mode (or editable mode) does NOT install the data files.
I honestly can’t think of any reason to do this.
More magic: pkg_resources
setuptools provides a pkg_resources system to access resources (such as data files) of the packages. It is a complex (and I think ugly) system, with lots of features.
But for just using it to find data files, it has some advantages – the primary one that is it can find data files that are inside zipped-up packages. (Python can import modules from zip files, but you can’t easily read data files from inside a zipped package)
To use pkg_resources, you include the files with package_data in setup.py but access them with the pkg_resources API:
Command line scripts
The “easy” and traditional way to isntall command line scripts is with the scripts keyword argument to the setup() command:
This works well on Unix systems (including the mac), but is not as reliable on Windows. All it really does is put a slightly altered copy of the script on PATH – so it will work if it is named with the .py extension and the system is set up to run .py files.
entry points
A more complicated, but better maintained and robust way is to use setuptools “entry points”. Entry points can provide a number of functions, but one of them is to make console scripts. Also an argument to setup() , It is done like so:
What this does is tell setuptools to make a little wrapper program called “script_name” that will start up python, and run the function called main in the package.module module.
Getting Started With a New Package
For anything but a single-file script (and maybe even then):
Create the basic package structure
Write a setup.py
Put some tests in package/test
pytest in the test dir, or pytest —pyargs package_name
or use “Cookie Cutter”:
LAB: A Small Example Package
Create a small package
python setup.py develop
at least one working test
Here is a ridiculously simple and useless package to use as an example:
Or go straight to making a package of your mailroom project.
© Copyright 2020, University of Washington, Natasha Aleksandrova, Christopher Barker, Brian Dorsey, Cris Ewing, Christy Heaton, Jon Jacky, Maria McKinley, Andy Miles, Rick Riehle, Joseph Schilz, Joseph Sheedy, Hosung Song. Creative Commons Attribution-ShareAlike 4.0 license.
What are Packages in Python and What is the Role of __init__.py files? (82/100 Days of Python)
In Python, the __init__.py file is a special file that serves several important purposes in a package. In this tutorial, we will explain what __init__.py files are, their meaning, and why they are needed in Python packages.
What is a package in Python?
Before diving into the details of __init__.py files, it's important to understand what a package is in Python. A package is a way to organize related modules (Python files) into a single, easy-to-use namespace. Packages allow you to group related functionality together, making it easier to organize and reuse your code.
A package in Python is simply a directory that contains a special file called __init__.py . The __init__.py file is executed when the package is imported, and it can contain any Python code you like.
What is the meaning of __init__.py files?
The __init__.py file has several meanings in Python. First and foremost, it is used to mark a directory as a package. When the Python interpreter encounters a directory that contains an __init__.py file, it treats the directory as a package and allows you to import modules from that package using the dot notation.
Secondly, the __init__.py file is used to initialize the package. This means that you can use the __init__.py file to set up any configuration or state that is needed by the package. For example, you can define package-level variables or import other modules that the package depends on.
Finally, the __init__.py file is used to control what symbols are exported from the package. When you import a module from a package, Python looks for the symbol in the module first, and then in the package's __init__.py file. This allows you to selectively import symbols from the package without cluttering up the namespace.
Why are __init__.py files needed?
__init__.py files are needed for several reasons. First and foremost, they allow you to organize your code into logical units called packages. This makes it easier to manage and reuse your code, and it also helps to avoid naming conflicts.
Secondly, __init__.py files are needed to control the import behavior of a package. By selectively importing symbols from a package, you can avoid name clashes and keep your code organized.
Finally, __init__.py files are often used to set up package-level configuration and state. This can include defining package-level variables or importing other modules that the package depends on.
Examples of __init__.py files
Here are some examples of how __init__.py files can be used in Python packages.
Defining package-level variables
In this example, we define a package-level variable called __version__ in the __init__.py file. This variable can be accessed from any module in the package using the dot notation ( mypackage.__version__ ). This is a common practice for library developers where they include the package version and some other metadata in the root of the package.
Importing modules into the package namespace
In this example, we import the mymodule module into the package namespace by using the dot notation ( from . import mymodule ). This allows us to access the contents of mymodule from any module in the package using the dot notation ( mypackage.mymodule ).
Defining a package-level function
In this example, we define a package-level function called my_package_function in the __init__.py file. This function can be accessed from any module in the package using the dot notation ( mypackage.my_package_function() ).
Controlling what symbols are exported from the package
In this example, we use the __all__ variable to control what symbols are exported from the package. We import the mymodule2 module into the package namespace using the star notation ( from .mymodule2 import * ) and then set __all__ to a list of symbols that should be exported ( __all__ = ['mymodule2'] ). This allows us to selectively import symbols from the package without cluttering up the namespace.
Файл пакета __init__.py
Если файл с именем __init__.py присутствует в каталоге пакета, то он вызывается при импорте пакета или модуля в пакете. Это может быть использовано для выполнения кода инициализации пакета, например инициализации данных уровня пакета.
Поместим следующий код в файл __init__.py :
Добавим файл __init__.py в каталог pkg из примера "Пакеты модулей в Python":
Теперь при импорте пакета инициализируется глобальная переменная var_init :
Модуль в пакете может получить доступ к глобальным переменным пакета файла __init__.py , импортируя его в свою очередь. Изменим файл модуля пакета mod1.py следующим образом:
Файл __init__.py может также использоваться для автоматического импорта модулей пакета. В материале "Пакеты модулей в Python" говорилось, что оператор import pkg помещает только имя пакета pkg в область видимости вызывающего объекта и не импортирует никакие модули. Изменим файл пакета __init__.py в каталоге pkg , дописав строку импорта модулей import pkg.mod1, pkg.mod2 .
Теперь при импорте пакета pkg , модули в mod1 и mod2 импортируются автоматически:
Примечание: большая часть статей про пакеты в Python утверждает, что файл __init__.py должен присутствовать в каталоге пакета при его создании. До версии Python 3.3 это было правдой. Само присутствие файла __init__.py означало для Python, что пакет был определен. Файл мог содержать код инициализации или даже быть пустым, но он должен был присутствовать.
Начиная с Python 3.3, были введены неявные пространств имен пакета, что позволяют создать пакет без каких-либо __init__.py файлов. Конечно, файл __init__.py все еще может присутствовать, если требуется инициализация пакета.