2

Googling around, I read many answers explaining that __all__ is for 'from some.package.name import *' , restricting importable modules to only ones inside it.

I'm absolutely sure it's true, if I want to import all the modules using wildcard from packages. But my question is about when I want to import some lower level objects like class, functions than modules, from package directly, the __all__ seems useless because to directly access the class from packages, it's the only way to import class from modules under the package into the __init__ file.

Here's example While doing some Django jobs, I recognized that Django-contributors really enjoy using __all__in the __init__.py files.

For example, in the basic Django package, models, __init__ files go like this.

django.db.model.__init__.py

---skipped---

from django.db.models.base import DEFERRED, Model  #

---skipped---

__all__ = aggregates_all + constraints_all + fields_all + indexes_all
__all__ += [
    '---skipped---'
    'Prefetch', 'Q', 'QuerySet', 'prefetch_related_objects', 'DEFERRED', 'Model',
]

You can see that here, 'Model' is definitely included __all__ ,but problem here is it's already imported upper lines, so if you don't include 'Model' in __all__, you can import it with from statement from django.db.models import *.

So I concluded that, in this case __all__ is redundant or needless, and that the purpose of writing 'Model' despite its redundancy is that for readability.

Do you think it's the right conclusion?

martineau
  • 119,623
  • 25
  • 170
  • 301
Now.Zero
  • 1,147
  • 1
  • 12
  • 23
  • Have you tested importing Model with `from django.db.models import *` without putting it inside of __all__? Also, this might help: https://stackoverflow.com/a/64130/6251569 – Gabriela Melo Mar 15 '20 at 14:18
  • Yep I tried that. I even set __all__ to None but it still works. – Now.Zero Mar 16 '20 at 07:26

1 Answers1

3

The tutorial about modules explains that __all__ is actually used to restrict what is imported when you use import *.

If __all__ is defined then when you import * from your package, only the names defined in __all__ are imported in the current namespace.

So it can be redundant, but only if you put in __all__ everything you import and define in your package.

Minimal working example:

testing/__init__.py:

from .moda import A
from .modb import B

__all__ = ['A']

testing/moda.py:

class A:
    pass

testing/modb.py:

class B:
    pass

Then when I import * from testing, I've got only A imported in current namespace, not B:

>>> from testing import *
>>> dir()
['A', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> 'B' in dir()
False
>>> 'A' in dir()
True
rolf82
  • 1,531
  • 1
  • 5
  • 15
  • Yep I tried that. I even set `__all__` to `None` but it still works.. – Now.Zero Mar 16 '20 at 07:25
  • Did you try with a simple minimal example? – rolf82 Mar 16 '20 at 07:29
  • I set `__all__` to None in `django.db.models` , and then in the script using Model, I wrote the statement `from django.db.models import *` and called Model in that file. It still works – Now.Zero Mar 16 '20 at 14:54
  • I meant a minimal ad-hoc example that you would have written, not based on django, because there could be other mechanisms in play – rolf82 Mar 16 '20 at 15:54
  • I edited the answer to include such an example. I admit I don't know what's going on with django but there may be other mechanisms we're not aware of. Nevertheless you've got the answer about the behavior of `__all__` in general. – rolf82 Mar 16 '20 at 18:07