Импорт по относительному пути в Python


106

У меня есть папка для моего клиентского кода, папка для моего серверного кода и папка для кода, который является общим для них

Proj/
    Client/
        Client.py
    Server/
        Server.py
    Common/
        __init__.py
        Common.py

Как импортировать Common.py из Server.py и Client.py?


Ответы:


142

ИЗМЕНИТЬ ноябрь 2014 г. (3 года спустя):

Python 2.6 и 3.x поддерживает правильный относительный импорт, при котором вы можете избежать любых взломов. С помощью этого метода вы знаете, что получаете относительный импорт, а не абсолютный . '..' означает, что нужно перейти в каталог выше меня:

from ..Common import Common

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

python -m Proj

Оригинальный хакерский способ

Этот метод все еще часто используется в некоторых ситуациях, когда вы фактически никогда не «устанавливаете» свой пакет. Например, он популярен среди пользователей Django.

Вы можете добавить Common / в свой sys.path (список путей, которые Python просматривает для импорта):

import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'Common'))
import Common

os.path.dirname(__file__) просто дает вам каталог, в котором находится ваш текущий файл python, а затем мы переходим к каталогу «Common /» и импортируем модуль «Common».


2
Не изменяйте путь к модулям Python вручную, это может быть только для быстрых взломов. Изучение управления пакетами Python с использованием distutils, setuptools и т. Д. Обычно является необходимым навыком для решения подобных проблем.
— Саша Готфрид

1
@SaschaGottfried полностью согласен, хотя, если вы не делаете распространяемый пакет, это, вероятно, не имеет значения. Например, в Django вы никогда не устанавливаете свое приложение с помощью distutils, поэтому описанный выше метод - легкий взлом. Но в любом случае я отредактировал ответ, указав, что я буду делать в эти дни.
— Дэйв

33
Спасибо за ответ на вопрос, а не за проповедь о правильной технике. Есть много веских причин для относительного импорта.
— shrewmouse

как бы вы поднялись более чем на один уровень?
— jxramos

10
чтобы подняться еще на один уровень, используйте дополнительную точку для каждого уровня. @jxramos ex: from ...myfileотправляется../../myfile
— WattsInABox

10

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

в сочетании с командой linux lnмы можем упростить задачу:

1. cd Proj/Client
2. ln -s ../Common ./

3. cd Proj/Server
4. ln -s ../Common ./

А теперь, если вы хотите импортировать some_stuffиз файла: Proj/Common/Common.pyв свой файл:, Proj/Client/Client.pyвот так:

# in Proj/Client/Client.py
from Common.Common import some_stuff

То же самое относится и к процессу Proj/Server, также работает для setup.pyпроцесса, тот же вопрос, который обсуждается здесь , надеюсь, это поможет!


10

Не выполняйте относительный импорт.

Из PEP8 :

Относительный импорт для импорта внутри упаковки крайне не рекомендуется.

Поместите весь свой код в один суперпакет (например, «myapp») и используйте подпакеты для клиента, сервера и общего кода.

Обновление: « Python 2.6 и 3.x поддерживает правильный относительный импорт (...) ». См . Ответы Дэйва для получения более подробной информации.


1
Представьте, что вы добавляете код в конце клиента и сервера после if __name__ == "__main__":строки " ". То есть вы хотите иметь возможность использовать их как отдельные скрипты. Как это правильно делать? Я думаю, что это вполне распространенный вариант использования, который следует поддерживать. Почему это не рекомендуется?
— Джабба

83
Я удивлен, что «Не делай этого» - это общепринятый ответ на вопрос «как мне ...» (ну, за исключением Rails <g>.) Иногда для этого есть причины. Я использую решение, подобное тому, что предлагает Дэйв.
— Том Уилсон

1
@TomWilson: Это не чистый ответ «не делай этого». Ниже указано «сделай это так».
— Michał Šrajer 01

2
Кто-то должен рассказать ребятам из Numpy! Они используют ТОННУ относительного импорта!
— Austin A

12
Этот ответ не применим к текущим версиям Python. Цитируемая часть больше не встречается в PEP 8. В настоящее время она читается так: «явный относительный импорт является приемлемой альтернативой абсолютному импорту, особенно при работе со сложными макетами пакетов, где использование абсолютного импорта было бы излишне многословным»
— moooeeeep

8

Выполнение относительного импорта абсолютно нормально! Вот что делает маленький я:

#first change the cwd to the script path
scriptPath = os.path.realpath(os.path.dirname(sys.argv[0]))
os.chdir(scriptPath)

#append the relative location you want to import from
sys.path.append("../common")

#import your module stored in '../common'
import common.py

1
Но вам лучше знать, куда на самом деле указывает sys.argv [0] - это (скорее всего) не тот каталог, в котором вы были, когда запускали python.
— CarlH

Это быстрый способ взлома с множеством подводных камней. Но вопрос был даже не лучше.
— Саша Готфрид

1
Это ясно написано, но исходный хак в ответе Дэйва лучше, потому что он использует __file__для получения правильной связи из текущего файла
— Джон Нойхаус

4

Метод импорта по умолчанию уже является "относительным" из PYTHONPATH. PYTHONPATH по умолчанию используется для некоторых системных библиотек вместе с папкой исходного исходного файла. Если вы запустите с -m для запуска модуля, текущий каталог будет добавлен в PYTHONPATH. Поэтому, если точка входа вашей программы находится внутри Proj, то использование import Common.Commonдолжно работать как внутри Server.py, так и в Client.py.

Не делайте относительного импорта. Это не сработает так, как вы хотите.


1
Если это правда, почему лучшие ответы не говорят этого? Это сработает или нет?
— Anonymous
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.