问题
First of all, there are a bunch of solutions on stackoverflow regarding this but from the ones I tried none of them is working. I am working on a remote machine (linux). I am prototyping within the dir-2/module_2.py
file using an ipython interpreter. Also I am trying to avoid using absolute paths as the absolute path in this remote machine is long and ugly, and I want my code to run on other machines upon download.
My directory structure is as follows:
/project-dir/
-/dir-1/
-/__ init__.py
-/module_1.py
-/dir-2/
-/__ init__.py
-/module_2.py
-/module_3.py
Now I want to import module_1
from module_2
. However the solution mentioned in this stackoverflow post: link of using
sys.path.append('../..')
import module_2
Does not work. I get the error: ModuleNotFoundError: No module named 'module_1'
Moreover, within the ipython interpreter things like import .module_3
within module_2
throws error:
import .module_3 ^ SyntaxError: invalid syntax
Isn't the dot operator supposed to work within the same directory as well. Overall I am quite confused by the importing mechanism. Any help with the initial problem is greatly appreciated! Thanks a lot!
回答1:
Why it didn't work?
If you run the module1.py
file and you want to import module2
then you need something like
sys.path.append("../dir-2")
If you use sys.path.append("../..") then the folder you added to the path is the folder containing
project-dirand there is not
module2.py` file inside it.
The syntax import .module_3
is for relative imports. if you tried to execute module2.py
and it contains import .module_3
it does not work because you are using module2.py
as a script. To use relative imports you need to treat both module2.py
and module_3.py
as modules. That is, some other file imports module2 and module2 import something from module3 using this syntax.
Suggestion on how you can proceed
One possible solution that solves both problems is property organizing the project and (optionally, ut a good idea) packaging your library (that is, make your code "installable"). Then, once your library is installed (in the virtual environment you are working) you don't need hacky sys.path
solutions. You will be able to import your library from any folder.
Furthermore, don't treat your modules as scripts (don't run your modules). Use a separate python file as your "executable" (or entry point) and import everything you need from there. With this, relative imports in your module*.py
files will work correctly and you don't get confused.
A possible directory structure could be
/project-dir/
- apps/
- main.py
- yourlib/
-/__ init__.py
-/dir-1/
-/__ init__.py
-/module_1.py
-/dir-2/
-/__ init__.py
-/module_2.py
-/module_3.py
Notice that the the yourlib
folder as well as subfolders contain an __init__.py
file. With this structure, you only run main.py
(the name does not need to be main.py
).
Case 1: You don't want to package your library
If you don't want to package your library, then you can add sys.path.append("../")
in main.py
to add "the project-dir/
folder to the path. With that your yourlib
library will be "importable" in main.py
. You can do something like from yourlib import module_2
and it will work correctly (and module_2
can use relative imports). Alternatively, you can also directly put main.py
in the project-dir/
folder and you don't need to change sys.path
at all, since project-dir/
will be the "working directory" in that case.
Note that you can also have a tests
folder inside project-dir
and to run a test file you can do the same as you did to run main.py
.
Case 2: You want to package your library
The previous solution already solves your problems, but going the extra mile adds some benefits, such as dependency management and no need to change sys.path
no matter where you are. There are several options to package your library and I will show one option using poetry due to its simplicity.
After installing poetry, you can run the command below in a terminal to create a new project
poetry new mylib
This creates the following folder structure
mylib/
- README.rst
- mylib/
- __init__.py
- pyproject.toml
- tests
You can then add the apps
folder if you want, as well as subfolders inside mylib/
(each with a __init__.py
file).
The pyproject.toml file specifies the dependencies and project metadata. You can edit it by hand and/or use poetry to add new dependencies, such as
poetry add pandas
poetry add --dev mypy
to add pandas
as a dependency and mypy
as a development dependency, for instance. After that, you can run
poetry build
to create a virtual environment and install your library in it. You can activate the virtual environment with poetry shell
and you will be able to import your library from anywhere. Note that you can change your library files without the need to run poetry build
again.
At last, if you want to publish your library in PyPi for everyone to see you can use
poetry publish --username your_pypi_username --password _passowrd_
TL; DR
Use an organized project structure with a clear place for the scripts you execute. Particularly, it is better if the script you execute is outside the folder with your modules. Also, don't run a module as a script (otherwise you can't use relative imports).
来源:https://stackoverflow.com/questions/64107888/importing-module-from-a-directory-above-current-working-directory