16

With a project structure like this:

├─main.py
└─src
    ├─dep1.py
    └─dep2.py

where the contents of each file is as follows:

main.py: import src.dep1 as firstdep; print("Total success")

dep1.py: import dep2 as seconddeb; print("success 1/3")

dep2.py: print("success 2/3")

Is the best way to do this creating an __init__.py file in src and importing src.dep2 in dep1.py? or is this a bad idea?

top 11 comments
sorted by: hot top controversial new old
[-] rtxn@lemmy.world 14 points 3 weeks ago* (last edited 3 weeks ago)

If you have to import a directory structure, you should make each directory a module by creating an __init__.py file in them, and use relative import statements. I usually have one main.py as the entry point, which imports a lib module that contains all of the program logic.

├── lib
│   ├── __init__.py
│   ├── module_content.py
│   └── submodule
│       ├── __init__.py
│       └── submodule_content.py
└── main.py

You can import the lib directory as a module:

main.py:

from lib import some_fn

Within any module, though, you should use relative import statements to import from files and submodules, and regular import statements to import packages from the system or the venv:

lib/__init__.py:

from .module_content import some_fn # import from a file
from .submodule import some_other_fn # import from a submodule directory
from os.path import join # import from an installed package

Items that you define in __init__.py or import into it will be available to import from the module: from .submodule import some_fn. Otherwise, you can import an item from a file by specifying the full path: from .submodule.submodule_content import some_fn.

You can also import an item from a parent package using the .. prefix: from ..some_other_submodule import some_fn.

[-] Limitless_screaming@kbin.earth 3 points 3 weeks ago

Items that you define in __init__.py or import into it will be available to import from the module: from .submodule import some_fn

That will be very useful. Thanks for your quick reply!

[-] rtxn@lemmy.world 5 points 3 weeks ago

Quick template: https://files.catbox.moe/g5tcy5.tar

Also, don't delete this post. Others might need this information too.

[-] Limitless_screaming@kbin.earth 3 points 3 weeks ago

ofc. It's weird how no one asked this before with how little help articles mention __init__.py.

[-] rtxn@lemmy.world 5 points 3 weeks ago

https://docs.python.org/3/tutorial/modules.html#packages

The __init__.py files are required to make Python treat directories containing the file as packages (unless using a namespace package, a relatively advanced feature). This prevents 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.

[-] Limitless_screaming@kbin.earth 0 points 3 weeks ago

Are you accusing me of not reading tfm? because I did, but was expecting this specific situation to be clarified on stackOverFlow, geeksForGeeks or somewhere similar. Since it seemed like this import pattern should be common.

[-] rtxn@lemmy.world 3 points 3 weeks ago

I was pointing out a part of the documentation that you may have overlooked. In good faith, I might add, because the documentation is comprehensive but horribly signposted. Everything I wrote in my first comment is there, but in a less human, more technical phrasing.

[-] Limitless_screaming@kbin.earth 3 points 3 weeks ago

Sorry, that was just a joke that came off wrong. I understand you were just trying to help.

[-] tunetardis@piefed.ca 3 points 3 weeks ago

As others have mentioned, I would add a src/__init__.py file to turn src into a package.

Then, inside dep1.py, you can do a relative import with:

from . import dep2  

I'm not sure why you're using as to make the module name longer? Usually, if I use it at all, I use it make things shorter. Anyway, once you've imported dep2, you can call dep2.some_fn(). You could also go:

from .dep2 import some_fn  
some_fn()  
[-] Limitless_screaming@kbin.earth 2 points 3 weeks ago

Yeah, that's the solution I asked about (last line of my question). I was just trying to make sure that this is ok and won't tangle up my code later on.

I'm not sure why you're using as to make the module name longer? Usually, if I use it at all, I use it make things shorter. Anyway, once you've imported dep2, you can call dep2.some_fn().

That was just for the example (clarifying order of import). The actual code has imports such as from src.formatter import Formatter as fmt; always making the name shorter.

Thanks for the suggestions

[-] tunetardis@piefed.ca 2 points 2 weeks ago

I was just trying to make sure that this is ok and won’t tangle up my code later on.

I guess the main issue there is Python doesn't like circular dependencies? Like if you try to import dep2 from dep1 and dep1 from dep2, you're going to have a problem. You'd essentially have to refactor your code to have a dep3 that both can import to use the common functionality I guess.

this post was submitted on 06 Nov 2025
16 points (100.0% liked)

Python

7589 readers
1 users here now

Welcome to the Python community on the programming.dev Lemmy instance!

📅 Events

PastNovember 2023

October 2023

July 2023

August 2023

September 2023

🐍 Python project:
💓 Python Community:
✨ Python Ecosystem:
🌌 Fediverse
Communities
Projects
Feeds

founded 2 years ago
MODERATORS