I am developing a rather complex application for my company following the object-oriented model in python3. The application contains several packages and sub-packages, each - of course - containing an __init__.py module.
I mostly used those __init__.py modules to declare generic classes for the package inside them, which are intended to be used as abstract templates for their respective package only.
My question is now: Is this a "nice" / "correct" / "pythonic" way to use the __init__.py module(s)? Or shall I rather declare my generic classes somewhere else?
To give an example, let's assume a package mypkg
:
mypkg.__init__.py
:
class Foo(object):
__some_attr = None
def __init__(self, some_attr):
self.__some_attr = some_attr
@property
def some_attr(self):
return self.__some_attr
@some_attr.setter
def some_attr(self, val):
self.__some_attr = val
mypkg.myfoo.py
:
from . import Foo
class MyFoo(Foo):
def __init__(self):
super().__init__("this is my implemented value")
def printme(self):
print(self.some_attr)
If you have setup.py in your project and you use find_packages() within it, it is necessary to have an __init__.py file in every directory for packages to be automatically found.
The __init__.py file makes Python treat directories containing it as modules. Furthermore, this is the first file to be loaded in a module, so you can use it to execute code that you want to run each time a module is loaded, or specify the submodules to be exported.
If you remove the __init__.py file, Python will no longer look for submodules inside that directory, so attempts to import the module will fail. Leaving an __init__.py file empty is considered normal and even a good practice, if the package's modules and sub-packages do not need to share any code.
The 'abc' module in Python library provides the infrastructure for defining custom abstract base classes. 'abc' works by marking methods of the base class as abstract.
You can always put everything into one source file. The reason for splitting the more complex code into separate modules or packages is to separate the things that are mutually related from things that are unrelated. The separated things should be as independent as possible on the rest. And it applies to any level: data structures, functions, classes, modules, packages, applications.
Inside the module or inside the package should apply the same rules. I agree with Bakuriu that __init__.py
should be closely related to the package infrastructure, but not neccessarily to the functionality of the module. My personal opinion is that the __init__.py
should be as simple as possible. The reason firstly is that everything should be as simple as possible but not simpler. Secondly, people reading the code of your package tend to think that way. They may not expect the unexpected functionality in __init__.py
. It would probably be better to create generic.py
inside the package for the purpose. It is easier to document the purpose of the module (say via its top docstring).
The better the separation is from the beginning, the better can the independent features be combined in future. You get better flexibility -- both for the usage of module inside the package and also for future modifications.
It depends by what is the API you want to provide. For example the collections
module in the standard library defines all the classes in the __init__.py
1.
And the standard library should be pretty "pythonic", whatever that means.
However it provides mostly a "module-like" interface. It's quite rare to see:
import collections.abc
If you already have subpackages you are probably better introducing a new subpackage.
If, currently, the use of the package doesn't actually depend on subpackages you might consider putting the code in the __init__.py
. Or put the code in some private module and simply import the names inside __init__.py
(this is what I'd prefer)
If you are only concerned with where it's better to put the Abstract Base Classes, as shown above (collections.abc
contains the abstract base classes of the collections
package), and as you can see from the standard library's abc
module, it's common to define an abc.py
submodule that contains them.
You may consider exposing them directly from the __init__.py
doing:
from .abc import *
from . import abc
# ...
__all__ = list_of_names_to_export + abc.__all__
inside your __init__.py
.
1 The actual implementation used is in C however: _collectionsmodule.c
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With