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

106

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

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

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

Нарисовался
источник
Связанный: stackoverflow.com/q/72852/1025391
moooeeeep

Ответы:

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процесса, тот же вопрос, который обсуждается здесь , надеюсь, это поможет!

Jacoolee
источник
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