Фактически ни один из ваших примеров не должен работать из коробки.
Давайте изменим bin/script.py
несколько:
#! /usr/bin/env python
import sys
print sys.path
from stuff import mainfile
Это должно дать что-то вроде
['.../project/bin', ...]
Traceback (most recent call last):
File "bin/script.py", line 6, in <module>
from stuff import mainfile
ImportError: No module named stuff
Только the directory of the script (не текущий каталог) добавляется sys.path
автоматически:
Как инициализируется при запуске программы первый элемент этого списка , path[0]
, - это каталог, содержащий скрипт, который использовался для вызова интерпретатора Python. * Если каталог сценария недоступен (например, если интерпретатор вызывается интерактивно или если скрипт считывается со стандартного ввода), path[0]
- это пустая строка, которая направляет Python в поисковые модули в текущем каталоге сначала. Обратите внимание, что каталог сценариев вставлен перед вставками в результате PYTHONPATH
.
Следовательно, нет stuff
модуля на sys.path
. Я не уверен в настройке вашей среды, но это канонический результат, когда дополнительные параметры не настроены (например, PYTHONPATH
).
, подобным образом,
from ..stuff import mainfile
приведет к классической ValueError: Attempted relative import in non-package
. Вам сообщают о том, что вы можете относить только относительный импорт по отношению к фактическим модулям. Поскольку внутри script
..
не относится к модулю (потому что сам сценарий является модулем верхнего уровня, так сказать), относительный импорт здесь не работает. Говоря с точки зрения Python, над сценарием нет модуля, поэтому ..
не ссылается на что-то ощутимое, если оно используется в контексте скрипта.
Обратите внимание, что это также означает, что он делает не помощь только сделать project
и project/bin
в самих модулях, понижая __init__.py
маркерные файлы. Относительный импорт в родительский сценария возможен только в том случае, если родительский сценарий на самом деле является чем-то вроде понятия python.
Это одна из причин, почему существует переключатель командной строки -m
, позволяющий запускать модуль из командной строки. Например, учитывая выше относительный импорт
python -m project.bin.script
делает трюк, но только если выполняется из правильного каталога (ProjectDir/..).
Эта проблема еще хуже
from project.stuff import mainfile
, поскольку каталог project
только автоматически sys.path
при запуске сценария из каталога выше project
и не указать основной скрипт для запуска:
cd <projectdir>/..
python -m project.bin.script
# works
cd <projectdir>
python -m bin.script
# does not work, because `sys.path` starts with <projectdir>
# and it's `stuff.mainfile` now, not `project.stuff.mainfile`.
Если вы хотите импортировать модули из project
в свой скрипт, исправьте sys.path
в соответствии с вашими потребностями:
import sys
import os
sys.path.insert(0, os.path.dirname(sys.path[0]))
from stuff import mainfile
Вы должны запустить скрипт из 'директории project' (или иметь' project' каталог в PYTHONPATH) –
Относительный импорт будет работать только тогда, когда все они были в том же пакете (т.е. в 'project' тоже был' __init __. py' тоже. – jonrsharpe