You kind of killed the riddle. My impression is also that this is natural in frameworks.
Frameworks components can grow to become quite complicated. Imagine how big Django’s form code must be! We already know, however, how to handle large codes: we divide 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 be called MarkdownPage
etc. If we used only one file, we would have a structure like below:
- 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 framework user 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 into __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
Like well said Phillip J. Eby:
a Python programmer [...] will probably get annoyed when typing Foo.Foo.someMethod
when it should just be Foo.someFunction
.
So, to help our users' lives, we import all classes into __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 tiny, and all the code fit elegantly in the page.py
. Gradually, it grew, and we decided to separate it into different files. In this case import everything in __init__.py
is even more important as it allows codes that used the old version to continue running in the new.
To keep the namespace clean
Suppose our class MarkdownPage
use a class MarkdownParser
. In the event that times only a great page.py
, MarkdownParser
will be available to anyone who imports page.py
. However, this is not ideal: MarkdownParser
is a detail of implementation.
By placing the MarkdownPage
in markdown.py
, we can only import what we want from her into the __init__.py
, cleaning the namespace.
Worth out of frameworks?
Well, that depends... on small projects, almost certainly not. But look, if your project is big and has a good architecture, it will be modular, then some of its components will be, for general purposes, frameworks what other parts of the same project will use, right? So, don’t be surprised if this pattern appears in other projects as well.
Excellent explanation. Thank you.
– ThiagoO