Представьте себе эту структуру каталогов:
app/
__init__.py
sub1/
__init__.py
mod1.py
sub2/
__init__.py
mod2.py
Я кодирую mod1, и мне нужно что-то импортировать из mod2. Как я должен это делать?
Я пробовал from ..sub2 import mod2, но получаю сообщение «Попытка относительного импорта без пакета».
Я погуглил, но нашел только "манипуляции с sys.path". Нет ли чистого пути?
Обновлено: все мои __init__.py в настоящее время пусты
Edit2: я пытаюсь сделать это, потому что sub2 содержит классы, которые являются общими для подпакетов (sub1, subX и т. д.).
Edit3: поведение, которое я ищу, такое же, как описано в PEP 366 (спасибо John B)
Это длинное объяснение, но посмотрите здесь: stackoverflow.com/a/10713254/1267156 Я ответил на очень похожий вопрос. У меня была такая же проблема до прошлой ночи.
Для тех, кто хочет загрузить модуль, расположенный по произвольному пути, см. Это: stackoverflow.com/questions/67631/…
В связи с этим, Python 3 изменит стандартную обработку импорта на абсолютную по умолчанию; относительный импорт должен быть явно указан.






Кажется, что все хотят сказать вам, что вы должны делать, а не просто отвечать на вопрос.
Проблема в том, что вы запускаете модуль как __main__, передавая mod1.py в качестве аргумента интерпретатору.
От PEP 328:
Relative imports use a module's __name__ attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to '__main__') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
В Python 2.6 они добавляют возможность ссылаться на модули относительно основного модуля. PEP 366 описывает изменение.
Обновлять: По словам Ника Коглана, рекомендуемой альтернативой является запуск модуля внутри пакета с помощью ключа -m.
Ответ здесь заключается в том, чтобы возиться с sys.path на каждой точке входа в вашу программу. Думаю, это единственный способ сделать это.
Рекомендуемая альтернатива - запускать модули внутри пакетов с помощью переключателя -m, а не напрямую указывать их имя файла.
Я не понимаю: где здесь ответ? Как можно импортировать модули в такую структуру каталогов?
@Tom: В этом случае mod1 будет from sub2 import mod2. Затем, чтобы запустить mod1 из приложения, выполните python -m sub1.mod1.
@XiongChiamiov: означает ли это, что вы не можете этого сделать, если ваш питон встроен в приложение, поэтому у вас нет доступа к переключателям командной строки python?
@MattJoiner: он работает, если вы запускаете mod1.py как python -m app.sub1.mod1 (из родительского каталога app) как Панкадж написал.
Чтобы было понятно, когда вы выполняете from .m import whatever в сценарии, который вы передаете в качестве аргумента интерпретатору, это в основном решает для from __main__.m import whatever. Что заставляет искать __main__/m.py.
«Кажется, каждый хочет сказать вам, что вы должны делать, а не просто отвечать на вопрос». Что ж, хороший. Вот так получится помощь в написании софта должен быть! Когда я летаю вопреки хорошей практике, Я хочу знать.
main.py
setup.py
app/ ->
__init__.py
package_a/ ->
__init__.py
module_a.py
package_b/ ->
__init__.py
module_b.py
python main.py.main.py делает: import app.package_a.module_amodule_a.py делает import app.package_b.module_bВ качестве альтернативы 2 или 3 могут использовать: from app.package_a import module_a
Это будет работать, пока у вас есть app в вашем PYTHONPATH. Тогда main.py мог быть где угодно.
Таким образом, вы пишете setup.py для копирования (установки) всего пакета приложения и подпакетов в папки python целевой системы, а main.py в папки сценариев целевой системы.
Отличный ответ. Есть ли способ импортировать таким образом без установки пакета в PYTHONPATH?
Рекомендуемая дополнительная литература: blog.habnab.it/blog/2013/07/21/python-packages-and-you
затем, однажды, нужно будет изменить имя приложения на test_app. что случилось бы? Вам нужно будет изменить все исходные коды, импортировать app.package_b.module_b -> test_app.package_b.module_b. это абсолютно ПЛОХАЯ практика ... И мы должны попытаться использовать относительный импорт внутри пакета.
def import_path(fullpath):
"""
Import a file with full path specification. Allows one to
import from anywhere, something __import__ does not do.
"""
path, filename = os.path.split(fullpath)
filename, ext = os.path.splitext(filename)
sys.path.append(path)
module = __import__(filename)
reload(module) # Might be out of date
del sys.path[-1]
return module
Я использую этот фрагмент для импорта модулей из путей, надеюсь, что это поможет
Я использую этот фрагмент в сочетании с модулем imp (как описано здесь [1]), чтобы добиться большого эффекта. [1]: stackoverflow.com/questions/1096216/…
Вероятно, sys.path.append (путь) следует заменить на sys.path.insert (0, путь), а sys.path [-1] следует заменить на sys.path [0]. В противном случае функция импортирует неправильный модуль, если в пути поиска уже есть модуль с таким же именем. Например, если в текущем каталоге есть some.py, import_path («/ imports / some.py») импортирует неправильный файл.
Я согласен! Иногда прецедент будет иметь другой относительный импорт. Используйте sys.path.insert
Как бы вы воспроизвели поведение from x import y (или *)?
Непонятно, укажите полное использование этого скрипта для решения проблемы OP.
От Документ Python,
In Python 2.5, you can switch import‘s behaviour to absolute imports using a
from __future__ import absolute_importdirective. This absolute- import behaviour will become the default in a future version (probably Python 2.7). Once absolute imports are the default,import stringwill always find the standard library’s version. It’s suggested that users should begin using absolute imports as much as possible, so it’s preferable to begin writingfrom pkg import stringin your code
Взгляните на http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports. Ты мог бы сделать
from .mod1 import stuff
За исключением того, что нельзя выполнять относительный импорт из модуля 'главный', как говорится в ответе Джона Б.
«Гвидо рассматривает запуск скриптов в пакете как антишаблон» (отклонено PEP-3122)
Я потратил так много времени, пытаясь найти решение, читая соответствующие сообщения здесь, в Stack Overflow, и говорил себе: «Должен быть способ получше!». Похоже, нет.
Примечание: уже упомянутый пеп-366 (созданный примерно в то же время, что и pep-3122) предоставляет те же возможности, но использует другую обратно-совместимую реализацию, т.е. если вы хотите запустить модуль внутри пакета как сценарий и, используйте в нем явный относительный импорт, тогда вы может запустить его с помощью переключателя -m: python -m app.sub1.mod1 или вызвать app.sub1.mod1.main() из сценария верхнего уровня (например, сгенерированного из точек входа setuptools, определенных в setup.py).
+1 за использование инструментов настройки и точек входа - это правильный способ настроить скрипты, которые будут запускаться извне в четко определенном месте, в отличие от бесконечного взлома PYTHONPATH
К сожалению, это взлом sys.path, но он работает довольно хорошо.
Я столкнулся с этой проблемой на другом уровне: у меня уже был модуль с указанным именем, но это был неправильный модуль.
Я хотел сделать следующее (модуль, с которым я работал, был module3):
mymodule\
__init__.py
mymodule1\
__init__.py
mymodule1_1
mymodule2\
__init__.py
mymodule2_1
import mymodule.mymodule1.mymodule1_1
Обратите внимание, что я уже установил mymodule, но в моей установке у меня нет «mymodule1»
и я получал ImportError, потому что он пытался импортировать из моих установленных модулей.
Я попытался сделать sys.path.append, но это не сработало. Что действительно работало, так это sys.path.insert
if __name__ == '__main__':
sys.path.insert(0, '../..')
Так что вроде взлома, но все заработало! Так что имейте в виду, если вы хотите, чтобы ваше решение было перекрыть другие пути, вам нужно использовать sys.path.insert (0, pathname), чтобы заставить его работать! Это было очень неприятным камнем преткновения для меня, многие люди говорят, что используют функцию "добавить" в sys.path, но это не работает, если у вас уже есть определенный модуль (я нахожу это очень странным поведением)
sys.path.append('../') отлично работает для меня (Python 3.5.2)
Я думаю, что это нормально, поскольку он локализует взлом исполняемого файла и не влияет на другие модули, которые могут зависеть от ваших пакетов.
Я обнаружил, что проще установить переменную среды "PYTHONPATH" в верхнюю папку:
bash$ export PYTHONPATH=/PATH/TO/APP
тогда:
import sub1.func1
#...more import
конечно, PYTHONPATH является «глобальным», но это еще не доставляло мне хлопот.
По сути, virtualenv позволяет вам управлять операторами импорта.
Вдобавок к тому, что сказал Джон Б., похоже, что установка переменной __package__ должна помочь, а не изменять __main__, что может испортить другие вещи. Но, насколько я мог проверить, он работает не совсем так, как должен.
У меня такая же проблема, и ни PEP 328, ни 366 не решают ее полностью, так как оба, к концу дня, нуждаются в том, чтобы голова пакета была включена в sys.path, насколько я мог понять.
Я также должен упомянуть, что я не нашел, как отформатировать строку, которая должна входить в эти переменные. Это "package_head.subfolder.module_name" что ли?
Позвольте мне просто поместить это здесь для моей справки. Я знаю, что это плохой код Python, но мне нужен был сценарий для проекта, над которым я работал, и я хотел поместить его в каталог scripts.
import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
Вот решение, которое мне подходит:
Я делаю относительный импорт как from ..sub2 import mod2
а затем, если я хочу запустить mod1.py, я перехожу в родительский каталог app и запускаю модуль, используя переключатель python -m как python -m app.sub1.mod1.
Настоящая причина, по которой эта проблема возникает с относительным импортом, заключается в том, что относительный импорт работает, принимая свойство __name__ модуля. Если модуль запускается напрямую, то для __name__ устанавливается значение __main__, и он не содержит никакой информации о структуре пакета. И поэтому python жалуется на ошибку relative import in non-package.
Итак, используя переключатель -m, вы предоставляете python информацию о структуре пакета, с помощью которой он может успешно разрешить относительный импорт.
Я сталкивался с этой проблемой много раз при выполнении относительного импорта. И, прочитав все предыдущие ответы, я все еще не мог понять, как решить эту проблему чистым способом, без необходимости помещать шаблонный код во все файлы. (Хотя некоторые комментарии были действительно полезными, спасибо @ncoghlan и @XiongChiamiov)
Надеюсь, это поможет кому-то, кто борется с проблемой относительного импорта, потому что проходить PEP действительно не весело.
Лучший ответ IMHO: не только объясняет, почему у OP возникла проблема, но и находит способ ее решения без изменения способа, которым его модули импортируют. В конце концов, относительный импорт OP был в порядке. Причина заключалась в отсутствии доступа к внешним пакетам при прямом запуске в качестве сценария, для решения которой был разработан -m.
Также обратите внимание: этот ответ был дан через 5 лет после вопроса. В то время эти функции были недоступны.
Если вы хотите импортировать модуль из того же каталога, вы можете сделать from . import some_module.
объяснение ответа nosklo's с примерами
примечание: все файлы __init__.py пусты.
main.py
app/ ->
__init__.py
package_a/ ->
__init__.py
fun_a.py
package_b/ ->
__init__.py
fun_b.py
def print_a():
print 'This is a function in dir package_a'
from app.package_a.fun_a import print_a
def print_b():
print 'This is a function in dir package_b'
print 'going to call a function in dir package_a'
print '-'*30
print_a()
from app.package_b import fun_b
fun_b.print_b()
если вы запустите $ python main.py, он вернет:
This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
from app.package_b import fun_bfrom app.package_a.fun_a import print_aпоэтому файл в папке package_b используется файл в папке package_a, что вы и хотите. Правильно??
Как говорит @EvgeniSergeev в комментариях к OP, вы можете импортировать код из файла .py в произвольное место с помощью:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Это взято из этот ТАК ответ.
Это решается на 100%:
Импортируйте настройки / local_setting.py в app / main.py:
main.py:
import sys
sys.path.insert(0, "../settings")
try:
from local_settings import *
except ImportError:
print('No Import')
Спасибо! все люди заставляли меня запускать мой скрипт по-другому, вместо того, чтобы рассказывать мне, как решить эту проблему в скрипте. Но мне пришлось изменить код, чтобы использовать sys.path.insert(0, "../settings"), а затем from local_settings import *.
Вы должны добавить путь модуля к PYTHONPATH:
export PYTHONPATH = "${PYTHONPATH}:/path/to/your/module/"
Это примерно то же самое, что манипулировать sys.path, поскольку sys.path инициализируется из PYTHONPATH.
@Joril Это правильно, но sys.path должен быть жестко закодирован в исходном коде, в отличие от PYTHONPATH, который является переменной среды и может быть экспортирован.
Я рекомендую обновить ваш вопрос, чтобы прояснить, что вы описываете проблему, рассматриваемую в PEP 366.