Импорт модулей из родительской папки - PullRequest
489 голосов
/ 03 апреля 2009

Я использую Python 2.5.

Это мое дерево папок:

ptdraft/
  nib.py
  simulations/
    life/
      life.py

(у меня также есть __init__.py в каждой папке, здесь опущено для удобства чтения)

Как импортировать модуль nib из модуля life? Я надеюсь, что можно обойтись без возни с sys.path.

Примечание. Основной модуль, который запускается, находится в папке ptdraft.

Ответы [ 17 ]

539 голосов
/ 03 апреля 2009

Вы можете использовать относительный импорт (python> = 2.5):

from ... import nib

(Что нового в Python 2.5) PEP 328: Абсолютный и относительный импорт

РЕДАКТИРОВАТЬ : добавлена ​​еще одна точка '.' подняться двумя пакетами

229 голосов
/ 22 июня 2012

Относительный импорт (как в from .. import mymodule) работает только в пакете. Чтобы импортировать «mymodule», который находится в родительском каталоге вашего текущего модуля:

import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir) 

import mymodule

edit : атрибут __file__ не всегда указан. Вместо использования os.path.abspath(__file__) я теперь предложил использовать модуль проверки для получения имени файла (и пути) текущего файла

101 голосов
/ 03 апреля 2009

Кажется, что проблема не связана с тем, что модуль находится в родительском каталоге или чем-то в этом роде.

Вам необходимо добавить каталог, содержащий ptdraft, в PYTHONPATH

Вы сказали, что import nib работал с вами, что, вероятно, означает, что вы добавили ptdraft сам (не его родитель) в PYTHONPATH.

62 голосов
/ 06 мая 2018

Я опубликовал аналогичный ответ и на вопрос об импорте из родственных пакетов. Вы можете видеть это здесь . Следующие тестируются с Python 3.6.5, (Anaconda, conda 4.5.1), машина Windows 10.

Решение без взлома sys.path

Настройка

Я предполагаю ту же структуру папок, что и в вопросе

.
└── ptdraft
    ├── __init__.py
    ├── nib.py
    └── simulations
        ├── __init__.py
        └── life
            ├── __init__.py
            └── life.py

Я называю . корневой папкой, и в моем случае она находится в C:\tmp\test_imports.

Steps

1) Добавьте файл setup.py в корневую папку

Содержимое setup.py может быть просто

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

В принципе "любой" файл setup.py будет работать. Это просто минимальный рабочий пример.

2) Использовать виртуальную среду

Если вы знакомы с виртуальными средами, активируйте одну и переходите к следующему шагу. Использование виртуальных сред не абсолютно требуется, но они действительно поможет вам в долгосрочной перспективе (когда у вас более 1 проекта ..). Основные шаги (запуск в корневой папке)

  • Создать виртуальную среду
    • python -m venv venv
  • Активировать виртуальную среду
    • . /venv/bin/activate (Linux) или ./venv/Scripts/activate (Win)

Чтобы узнать больше об этом, просто посмотрите в Google "Python Virtual Env Tutorial" или подобное. Вам, вероятно, никогда не понадобятся какие-либо другие команды, кроме создания, активации и деактивации.

После того, как вы создали и активировали виртуальную среду, ваша консоль должна указать имя виртуальной среды в скобках

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

3) pip установите ваш проект в редактируемое состояние

Установите пакет верхнего уровня myproject, используя pip. Хитрость заключается в использовании флага -e при установке. Таким образом, он устанавливается в редактируемом состоянии, и все изменения, внесенные в файлы .py, будут автоматически включены в установленный пакет.

В корневом каталоге запустите

pip install -e . (обратите внимание на точку, она означает «текущий каталог»)

Вы также можете увидеть, что он установлен с помощью pip freeze

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

4) Импорт путем добавления mainfolder к каждому импорту

В этом примере mainfolder будет ptdraft. Преимущество этого заключается в том, что вы не столкнетесь с конфликтами имен с другими именами модулей (из стандартной библиотеки Python или сторонних модулей).


Пример использования

nib.py

def function_from_nib():
    print('I am the return value from function_from_nib!')

life.py

from ptdraft.nib import function_from_nib

if __name__ == '__main__':
    function_from_nib()

Запуск life.py

(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!
50 голосов
/ 17 февраля 2012

Если добавление папки вашего модуля в PYTHONPATH не сработало, вы можете изменить список sys.path в вашей программе, где интерпретатор Python ищет модули для импорта, документацию python говорит:

Когда импортируется модуль с именем spam , интерпретатор сначала ищет встроенный модуль с таким именем. Если он не найден, он ищет файл с именем spam.py в списке каталогов, заданных переменной sys.path. sys.path инициализируется из следующих мест:

  • каталог, содержащий входной скрипт (или текущий каталог).
  • PYTHONPATH (список имен каталогов с тем же синтаксисом, что и для переменной оболочки PATH).
  • зависящее от установки значение по умолчанию.

После инициализации программы Python могут изменять sys.path . Каталог, содержащий выполняемый скрипт, помещается в начало пути поиска, перед стандартным путем к библиотеке. Это означает, что скрипты в этом каталоге будут загружены вместо модулей с одинаковыми именами в каталоге библиотеки. Это ошибка, если замена не предназначена.

Зная это, вы можете делать в своей программе следующее:

import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')

# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft
40 голосов
/ 25 февраля 2015

Вы можете использовать путь, зависящий от ОС, в «пути поиска модуля», который указан в sys.path . Таким образом, вы можете легко добавить родительский каталог, например:

import sys
sys.path.insert(0,'..')

Если вы хотите добавить родительский каталог,

sys.path.insert(0,'../..')
34 голосов
/ 14 февраля 2017

Не много знаю о Python 2.
В Python 3 родительская папка может быть добавлена ​​следующим образом:

import sys 
sys.path.append('..')

... и тогда можно импортировать модули из него

29 голосов
/ 05 ноября 2015

Вот простой ответ, чтобы вы могли увидеть, как он работает, маленький и кроссплатформенный.
Он использует только встроенные модули (os, sys и inspect), поэтому должен работать
в любой операционной системе (ОС), потому что Python предназначен для этого.

Сокращенный код для ответа - меньше строк и переменных

from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module  # Replace "my_module" here with the module name.
sys.path.pop(0)

Для меньшего количества строк, чем это, замените вторую строку на import os.path as path, sys, inspect,
добавьте inspect. в начале getsourcefile (строка 3) и удалите первую строку.
- однако при этом импортируются все модули, поэтому может потребоваться больше времени, памяти и ресурсов.

Код моего ответа ( длинная версия )

from inspect import getsourcefile
import os.path
import sys

current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]

sys.path.insert(0, parent_dir)

import my_module  # Replace "my_module" here with the module name.

Используется пример из ответа переполнения стека Как получить путь к текущему
исполняемый файл на Python?
, чтобы найти источник (имя файла) исполняемого кода с помощью встроенного инструмента.

from inspect import getsourcefile  
from os.path import abspath  

Далее, где бы вы ни хотели найти исходный файл, просто используйте:

abspath(getsourcefile(lambda:0))

Мой код добавляет путь к файлу sys.path, список путей python
потому что это позволяет Python импортировать модули из этой папки.

После импорта модуля в коде рекомендуется запустить sys.path.pop(0) на новой строке
когда в этой добавленной папке есть модуль с тем же именем, что и другой импортируемый модуль
позже в программе. Вам нужно удалить элемент списка, добавленный перед импортом, а не другие пути.
Если ваша программа не импортирует другие модули, безопасно не удалять путь к файлу, потому что
после завершения программы (или перезапуска оболочки Python) любые изменения, сделанные в sys.path, исчезают.

Замечания о переменной имени файла

В моем ответе не используется переменная __file__ для получения пути к файлу / имени файла для запуска
код, потому что пользователи здесь часто описывают его как ненадежный . Вы не должны использовать это
для импорта модулей из родительской папки в программах, используемых другими людьми.

Некоторые примеры, где это не работает (цитата из this Вопрос переполнения стека):

• это не может быть найдено на некоторых платформах • иногда не полный путь к файлу

  • py2exe не имеет атрибута __file__, но есть обходной путь
  • При запуске из IDLE с execute() отсутствует атрибут __file__
  • OS X 10.6, где я получаю NameError: global name '__file__' is not defined
27 голосов
/ 14 февраля 2014

Вот более общее решение, которое включает родительский каталог в sys.path (работает для меня):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
10 голосов
/ 12 апреля 2018

Для меня самый короткий и мой любимый oneliner для доступа к родительскому каталогу:

sys.path.append(os.path.dirname(os.getcwd()))

или

sys.path.insert(1, os.path.dirname(os.getcwd()))

os.getcwd () возвращает имя текущего рабочего каталога, os.path.dirname (directory_name) возвращает имя каталога для переданного.

На самом деле, на мой взгляд, архитектура проекта Python должна выполняться так, чтобы ни один модуль из дочернего каталога не использовал ни один модуль из родительского каталога. Если что-то подобное происходит, стоит задуматься о дереве проекта.

Другой способ - добавить родительский каталог в системную переменную среды PYTHONPATH.

...