Імпортування з відносного шляху в Python


104

У мене є папка для мого клієнтського коду, папка для мого серверного коду та папка для коду, який спільно використовується між ними

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

Як імпортувати Common.py із Server.py та Client.py?


Відповіді:


140

EDIT листопада 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, тому вищевказаний спосіб є простим злому. Але в будь-якому випадку я відредагував відповідь тим, чим би займався сьогодні.
— Дейв

32
Дякуємо, що відповіли на актуальне запитання, а не проповідували про правильну техніку. Існує маса вагомих причин для відносного імпорту.
— землерийка

як би ви піднялися більше ніж на один рівень?
— 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з file: 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 <г> крім.) Там є випадкові причини , щоб зробити це. Я використовую рішення, подібне до того, що пропонує Дейв.
— Том Вілсон,

1
@TomWilson: Це не чиста відповідь "не роби цього". Нижче є "зробити це таким чином".
— Michał Šrajer

2
Хтось повинен сказати хлопцям у Numpy! Вони використовують ТОН відносного імпорту!
— Остін А

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] - він (prolly) не є каталогом, в якому ви знаходились, коли запускали python.
— CarlH

Це швидкий хак, з великою кількістю підводних каменів. Але питання було навіть не кращим.
— Саша Готфрід

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

4

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

Не робіть відносного імпорту. Це не спрацює так, як ти хочеш.


1
Якщо це правда, чому в основних відповідях цього не сказано? Це спрацює чи ні?
— Анонім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.