Как импортировать модули из файла в более глубоком каталоге? - PullRequest
2 голосов
/ 20 апреля 2020

скажем, структура моего проекта выглядит следующим образом:

project
├── important.py
└── files
    └── file1.py

, а файл важный.py содержит класс Important. Как я могу импортировать класс (Важно) из файла1, в то время как файл1 - это файл python, который выполняется? .

Единственное решение, которое я нашел, было добавить этот код, но я Интересно, есть ли более чистый способ:

import sys; sys.path.append("..")
from important import Important

То, что я пробовал без успеха:

from project.important import Important
# ModuleNotFoundError: No module named 'project'
# But it does work inside PyCharm (Why is that?)
from ..important import Important
# ValueError: attempted relative import beyond top-level package

И эти ошибки продолжали отображаться, даже если я добавил __init__.py Файл в каталоге проекта.

Скажу, что я ищу решение, которое подойдет для любой машины, так как я хочу поделиться этим проектом на github с publi c.

Ответы [ 3 ]

1 голос
/ 20 апреля 2020

Вам нужно будет сделать ссылку на родительскую папку в пределах sys.path. Это можно сделать явно внутри кода, как вы это сделали, что на самом деле не pythoni c. Это также может быть сделано извне кода, например, путем изменения системной переменной PYTHONPATH или путем установки вашего модуля в python.

. Я настоятельно не рекомендую использовать абсолютный путь, как предлагают другие ответы, потому что тогда код будет работать только на вашем компьютере. Это терпимо для студенческих проектов, но это плохая практика в реальной жизни, поскольку над ней будут работать несколько человек, она будет выполняться на серверах тестирования / производства / песочницы, и т. Д. c ...

подход правильный. Однако я все же предлагаю немного изменить ваш синтаксис, поскольку в некоторых случаях он не будет работать должным образом:

all_projects/
└── current_project/
    ├── important.py
    └── files
        └── file1.py

$ cd /path/to/all_projects/current_project/files/
$ python file1.py
####  > Ok, thanks to the line sys.path.append("..")

$ cd /path/to/all_projects/current_project/
$ python files/file1.py
####  > Ok, because python implicitly add the execution path to sys.path

$ cd /path/to/all_projects/
$ python current_project/files/file1.py
#### > ModuleNotFoundError: No module named 'important'

Вместо этого используйте следующее:

import sys, os
sys.path.append(os.path.dirname(sys.path[0]))

Или, если file1.py может быть даже импортирован из другого файла, следующее даже безопаснее:

import sys, os
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

Наконец, для хорошей практики разработки я также предлагаю поместить эти строки в другой файл, который называется, например, _set_path. Причина в том, что вы можете использовать это в будущих файлах, и если вы хотите изменить архитектуру кода, вам нужно изменить только один файл:

        ├── file1.py
        ├── file2.py
        ├── ...
        └── _set_path.py

Затем из file1.py вы можете использовать:

import _set_path
from important import Important

(Ответ основан на Python: Лучший способ добавить в sys.path относительно текущего запущенного скрипта )

1 голос
/ 20 апреля 2020

Люди указали маршрут sys.path.append(".."). Хотя это работает, существует также альтернативный метод с os.chdir('..')

. Вы можете просмотреть список путей с помощью следующей команды python3 -m site. При импорте пакетов Python проверяет наличие модулей во всех этих путях.
Первый элемент в вашем sys.path является текущим рабочим каталогом.

Может быть сценарий, когда вы не хотите ваш текущий рабочий каталог должен быть частью пути и вы хотите, чтобы одна структура папок была добавлена ​​к пути.

«Проблема» в том, что существует несколько способов импорта одного и того же. Например, у вас есть:

project/
├── important.py
└── files
    ├── file1.py
    └── file2.py

Выполнив sys.path.append("..") и запустив программу через python3 file1.py, вы можете импортировать файл2 через import file2 или from files import file2. Это выглядит не очень хорошо, и вы можете начать писать несовместимый код, не понимая, как правильно работает импорт.

Вы можете придерживаться sys.path.append(".."), если он работает. Вы не будете делать много плохого с этим. Это общий подход многих людей. Может быть просто особый сценарий, в котором вы можете столкнуться с проблемами, поэтому многие люди предпочитают подход os.chdir().

Например, в папке, верхней папке и подпапке у вас есть python модули, которые совместно используют одно и то же имя Вы хотите импортировать Python модулей из одной папки вверх, но не python модулей в текущей папке.


Пример os.chdir() в действии:

Tin@ubuntu:~/Desktop/tmp/test$ python3
Python 3.6.8 (default, Oct  7 2019, 12:59:55) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.getcwd()
'/home/Tin/Desktop/tmp/test'
>>> import helloworld
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'helloworld'
>>> os.chdir('..')
>>> import helloworld
hello world!
>>> os.getcwd()
'/home/Tin/Desktop/tmp'

Теперь вы можете импортировать из одного каталога вверх, и импорт больше не является неоднозначным.

Обратите внимание, что пока я писал os.chdir('..'), вы можете делать то, что делал @Tawy.

import os
os.chdir(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
# OR:
os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))

Это выглядит запутанно но с этим вы можете внутренне представить, какой у вас текущий рабочий каталог, и основывать на этом все ваши операторы импорта. Он также дает вам согласованный текущий рабочий каталог, когда вы выполняете свой скрипт из всех видов подкаталогов, но ожидаете определенного рабочего каталога.
Вы также можете сделать ошибку, запустив os.chdir('..') дважды, что сделает вас go двумя структура папок:


Короче:

Наименее сложное решение - sys.path.append(".."). Более чистое решение будет os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')), где .. будет относительным местоположением для вашего требуемого рабочего каталога.

0 голосов
/ 20 апреля 2020

Вместо:

import sys

sys.path.append("..")

Вы можете сделать:

import sys

sys.path.append("/path/to/project")

То же самое, что и первый, но менее запутанно, так как вы добавляете абсолютный путь и делаете это очевидно для пользователя.

Обратите внимание, что вам не нужен файл __init__.py для работы вышеуказанного. Кроме того, обратите внимание, что это может быть не единственный способ, но я считаю, что он самый чистый.

Кроме того, если вы действительно ненавидите это решение, вы можете подумать о реструктуризации проекта, чтобы вы могли избежать этого сценария.

...