2015-07-07 4 views
0

У меня есть следующая структурапитона относительной структура импорта

project/ 
     bin/ 
      script 
     stuff/ 
      __init__.py 
      mainfile.py 

Внутри скрипта каталога есть следующее, чтобы включить команду выполнения строки:

#!/usr/bin/env python 
from stuff import mainfile 

Это работает, но я ожидал бы нуждаясь прыгать вверх на один уровень ...

from ..stuff import mainfile 

или

from project.stuff import mainfile 

Что мне здесь не хватает?

+0

Вы должны запустить скрипт из 'директории project' (или иметь' project' каталог в PYTHONPATH) –

+0

Относительный импорт будет работать только тогда, когда все они были в том же пакете (т.е. в 'project' тоже был' __init __. py' тоже. – jonrsharpe

ответ

0

Фактически ни один из ваших примеров не должен работать из коробки.

Давайте изменим 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 
+0

Это очень тщательный ответ, но он не объясняет, почему он работает так, как есть. Я протестировал файл, клонировав его в тестовый каталог и выполнив скрипт в командной строке, и он работает без проблем ... оставив меня очень смущенным. – Solaxun

+0

Я понятия не имею, почему это работает для вас, поскольку это явно не должно быть без дополнительной настройки. У вас что-нибудь есть на 'PYTHONPATH'? Возможно, ваш дистрибутив автоматически устанавливает «PYTHONPATH =.» (По любой причине). Обратите внимание, что просто печать 'sys.path' в' script' также может пролить свет. – dhke

+0

Интересно, имеет ли мой файл setup.py что-нибудь с этим? Я довольно зеленый с упаковкой/распределением ... Я буду отмечать это как правильное и сделать некоторые копания, как вы предложите позже. Спасибо. – Solaxun

0

Необходимо сначала добавить родительский каталог в sys.path. Попробуйте следующее:

# This is a file in bin/ directory. 
this_file_path = os.path.dirname(__file__) 
sys.path.append(os.path.join(this_file_path, '..')) 
import stuff.mainfile 
Смежные вопросы