You kind of killed the charade. My impression is also that this is natural in frameworks.
Framework components can grow to become very complicated. Imagine how Django forms code should be great! We already know, however, how to deal with large codes: we split them into several files.
Why create multiple files
Consider a hypothetical framework, for example, that has several types of classes that represent Web pages. All will inherit from a base class called Page
; there will be a class that only serves static content of a file, and will be called ResourcePage
; another class will have several facilities for JavaScript and CSS, and will be called RichPage
; a third one can read Markdown and generate HTML and will call MarkdownPage
etc. If we used a single file, we would have a structure like this:
- framework
- page.py
And page.py
would have several classes:
class Page(object):
# ...
pass
class ResourcePage(Page):
# ...
pass
class RichPage(Page):
# ...
pass
class MarkdownPage(Page):
# ...
pass
# E assim por diante...
The user of the framework can write something simple with from framework.page import RichPage
, but certainly page.py
will be huge! An alternative is to create multiple files:
- framework/
- page/
- page.py
- resource.py
- rich.py
- markdown.py
- __init__.py
The code will be much more maintainable, right?
Because you want to import classes in __init__.py
To facilitate
The code will be more maintainable, but now our poor user should import classes like this:
from framework.page.rich import RichPage
How well well said Phillip J. Eby :
A Python programmer [...] will probably get annoyed when typing Foo.Foo.someMethod
when it should only be Foo.someFunction
.
So, to help the lives of our users, we import all classes in __init__.py
:
from .page import Page
from .resource import ResourPage
from .rich import RichPage
from .markdown import MarkdownPage
Now our user can, happily, just invoke:
from framework.page import MarkdownPage
Not to break what already worked
Sometimes the main reason for this is backward compatibility. Our framework was small, and all the code fit elegantly in page.py
. Gradually, it grew, and we resolved to separate it into different files. In this case importing everything in __init__.py
is even more important, since it allows codes that used the old version to continue running on the new one.
To leave the namespace clean
Suppose our MarkdownPage
class uses a MarkdownParser
class. In the case where times only a large page.py
, MarkdownParser
will be available to those who import page.py
. However, this is not ideal: MarkdownParser
is an implementation detail.
By putting MarkdownPage
to markdown.py
, we can import only what we want from it into __init__.py
, clearing the namespace .
Is it worth out of frameworks?
Well, that depends ... On small projects, almost certainly not. But look, if your project is large and has a good architecture, it will be modular, so some components of it will be, for general purposes, frameworks that other parts of the same project will use, right? So, do not be surprised if this pattern appears on other projects as well.