Python Forum

Full Version: Do packages really require "__init__.py" ?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello,

In the official documentation for Python 3 it is written that :

Quote:The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later.

However, when a create a package without the presence of the file "__init__.py", it "works". I means that Python knows that it is a package even if the file "__init__.py" does not exist.

Demonstration:

Let's consider the following file tree:

├── package
│   ├── __init__.py
│   ├── module1.py
│   ├── module2.py
│   └── module3.py
└── test1.py


And let's consider the following code (the content of the file test1.py):

import os
import sys
from pprint import pformat

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import package
print('What is a package ? %s' %pformat(package))
print('Do packages have a property called "__path__" ? %s' %('yes' if hasattr(package, '__path__') else 'no'))
print('package.__path__ = %s' %package.__path__)
print('')

from package import module1
print('What is a module ? %s' %pformat(module1))
print('Do modules have a property called "__path__" ? %s' %('yes' if hasattr(module1, '__path__') else 'no'))
print('')
Now run the script test1.py:

With the file "package/__init__.py":

Output:
$ ls package/__init__.py && python3 test1.py package/__init__.py What is a package ? <module 'package' from '/home/denis/projects/imap/package_vs_module/package/__init__.py'> Do packages have a property called "__path__" ? yes package.__path__ = ['/home/denis/projects/imap/package_vs_module/package'] What is a module ? <module 'package.module1' from '/home/denis/projects/imap/package_vs_module/package/module1.py'> Do modules have a property called "__path__" ? no
And without the file "package/__init__.py":

Output:
$ rm -f package/__init__.py && rm -rf package/__pycache__ && python3 test1.py What is a package ? <module 'package' (namespace)> Do packages have a property called "__path__" ? yes package.__path__ = _NamespacePath(['/home/denis/projects/imap/package_vs_module/package', '/home/denis/projects/imap/package_vs_module/package']) What is a module ? <module 'package.module1' from '/home/denis/projects/imap/package_vs_module/package/module1.py'> Do modules have a property called "__path__" ? no
Conclusion:

Python recognises "package" as being a package event without the presence of the file "__init__.py".

Do I misunderstand something ?

Thanks,

Denis
I means that Python knows that it is a package even if the file "__init__.py" does not exist.
Sooner or later you will run into an issue where an import cannot be located, and your software will fail.
You can wait for that to happen, or buy some insurance by creating the __init__.py files.
Personally, I always create them.
(Feb-14-2019, 04:19 PM)denis_beurive Wrote: [ -> ]Python recognises "package" as being a package event without the presence of the file "__init__.py".

Do I misunderstand something ?
No,from Python 3.3+ supports Implicit Namespace Packages that allows to create a package without an __init__.py file.
This however only applies to empty __init__.py files.
So empty __init__.py files are no longer necessary and can be omitted.

To give a example,i always have one __init__.py that has content to lift sub modules,
this to avoid long import statement and make it easier for users of package.
my_pack\
|-- __init__.py
  color\
  |-- base_color.py
    gradient\    
    |-- gradient.py
__init__.py
from .color import base_color
from .color.gradient import gradient
base_color.py
def red():
    return 'rgb(255, 0, 0)'
gradient.py
def hsl_value():
    return 'rgba(255, 100, 50, 1.0)'
As i have lifted with __init__.py there is no need to say color.gradient.hsl_value() when import.
Usage:
λ ptpython
>>> from my_pack import base_color, gradient

>>> base_color.red()
'rgb(255, 0, 0)'


>>> gradient.hsl_value()
'rgba(255, 100, 50, 1.0)'
And stop using very old %s string formatting.
>>> for word in 'f-strings are awesome'.split():
...     print(f'{word.upper():~^20}')
~~~~~F-STRINGS~~~~~~
~~~~~~~~ARE~~~~~~~~~
~~~~~~AWESOME~~~~~~~

>>> # f-strings support any Python expressions inside the curly braces
>>> name = 'f-string'
>>> print(f"My cool string is called {name.upper()}.")
My cool string is called F-STRING.
 
>>> a, b = 5, 7
>>> f'{a}/{b} = {a/b:.2}'
'5/7 = 0.71'
(Feb-15-2019, 03:59 AM)snippsat Wrote: [ -> ]snippsat

Thank you for your response. It's clear.

Denis
Python 3.7.2, Eclipse IDE 2018‑12, Kubuntu 18.10 x64

If I use in Database/Tables/__init__.py line of code:
__all__ = ["Career", "Videos"]
, then in other packages this code:
from Database.Tables import Career
causes an error:
Error:
module Database.Tables has no attribute 'Career'
But if I comment line
#__all__ = ["Career", "Videos"]
this error dissapear.

Why?