Импорт модулей из родительской папки
Я использую Python 2.5.
Это мое дерево папок:
ptdraft/
nib.py
simulations/
life/
life.py
(У меня тоже есть __init__.py
в каждой папке здесь опущено для удобства чтения)
Как мне импортировать nib
модуль изнутри life
модуль? Я надеюсь, что можно обойтись без возиться с sys.path.
Примечание. Основной модуль, который запускается, находится в ptdraft
папка.
36 ответов
Кажется, что проблема не связана с тем, что модуль находится в родительском каталоге или чем-то в этом роде.
Вам нужно добавить каталог, который содержит ptdraft
ПИТОНАТУ
Вы сказали, что import nib
работал с вами, это, вероятно, означает, что вы добавили ptdraft
сам (не его родитель) PYTHONPATH.
Вы можете использовать относительный импорт (python> = 2.5):
from ... import nib
(Что нового в Python 2.5) PEP 328: Абсолютный и относительный импорт
РЕДАКТИРОВАТЬ: добавил еще одну точку "." пройти два пакета
Я опубликовал аналогичный ответ также на вопрос об импорте из родственных пакетов. Вы можете увидеть это здесь. Следующее тестируется на Python 3.6.5, (Anaconda, conda 4.5.1), машина с Windows 10.
Решение без sys.path -hacks или относительного импорта
Настроить
Я предполагаю ту же структуру папок, что и в вопросе
.
└── ptdraft
├── __init__.py
├── nib.py
└── simulations
├── __init__.py
└── life
├── __init__.py
└── life.py
Я звоню .
корневая папка, и в моем случае она находится в C:\tmp\test_imports
,
меры
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
(Выиграть)
Чтобы узнать больше об этом, просто посмотрите в 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!
Относительный импорт (как в 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
редактировать: __file__
атрибут не всегда дается. Вместо того, чтобы использовать os.path.abspath(__file__)
Теперь я предложил использовать модуль inspect для получения имени файла (и пути) текущего файла.
Вы можете использовать путь, зависящий от ОС, в "пути поиска модуля", который указан в sys.path. Таким образом, вы можете легко добавить родительский каталог следующим образом
import sys
sys.path.insert(0,'..')
Если вы хотите добавить родительский каталог,
sys.path.insert(0,'../..')
Не знаю много о Python 2.
В python 3 родительская папка может быть добавлена следующим образом:
import sys
sys.path.append('..')
... и тогда можно импортировать модули из него
Если добавление папки вашего модуля в 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
Вот простой ответ, чтобы вы могли увидеть, как он работает, маленький и кроссплатформенный.
Используются только встроенные модули (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 импортировать модули из этой папки.
После импорта модуля в коде рекомендуется запустить sys.path.pop(0)
на новой линии
когда в этой добавленной папке есть модуль с тем же именем, что и другой импортируемый модуль
позже в программе. Вам нужно удалить элемент списка, добавленный до импорта, а не другие пути.
Если ваша программа не импортирует другие модули, безопасно не удалять путь к файлу, потому что
после завершения программы (или перезапуска оболочки Python) любые изменения, внесенные в sys.path
исчезают.
Примечания о переменной имени файла
Мой ответ не использует __file__
переменная, чтобы получить путь к файлу / имя файла запуска
код, потому что пользователи здесь часто называют его ненадежным. Вы не должны использовать это
для импорта модулей из родительской папки в программы, используемые другими людьми.
Некоторые примеры, где это не работает (цитата из этого вопроса переполнения стека):
• его нельзя найти на некоторых платформах • иногда это не полный путь к файлу
py2exe
не имеет__file__
атрибут, но есть обходной путь- Когда вы бежите из IDLE с
execute()
здесь нет__file__
атрибут- OS X 10.6, где я получаю
NameError: global name '__file__' is not defined
Когда не в пакетной среде с __init__.py
Файлы библиотеки pathlib (включены в>= Python 3.4) делают ее очень лаконичной и интуитивно понятной для добавления пути родительского каталога к PYTHONPATH:
import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))
Вот более общее решение, которое включает родительский каталог в sys.path (работает для меня):
import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
В записной книжке Jupyter
Пока вы работаете в Jupyter Notebook, это короткое решение может оказаться полезным:
%cd ..
import nib
Работает даже без __init__.py
файл.
Я тестировал его с Anaconda3 в Linux и Windows 7.
Я обнаружил, что следующий способ работает для импорта пакета из родительского каталога скрипта. В этом примере я хотел бы импортировать функции в env.py
от app.db
пакет.
.
└── my_application
└── alembic
└── env.py
└── app
├── __init__.py
└── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
Вышеупомянутые решения тоже подойдут. Другое решение этой проблемы
Если вы хотите импортировать что-либо из каталога верхнего уровня. Затем,
from ...module_name import *
Также, если вы хотите импортировать какой-либо модуль из родительского каталога. Затем,
from ..module_name import *
Также, если вы хотите импортировать какой-либо модуль из родительского каталога. Затем,
from ...module_name.another_module import *
This way you can import any particular method if you want to.
Двухстрочное простейшее решение
import os, sys
sys.path.insert(0, os.getcwd())
Если родительский - ваш рабочий каталог, и вы хотите вызвать другие дочерние модули из дочерних скриптов.
Вы можете импортировать все дочерние модули из родительского каталога в любые скрипты и выполнять их как
python child_module1/child_script.py
Хотя первоначальный автор, вероятно, больше не ищет решения, но для полноты картины есть одно простое решение. Это запустить life.py как такой модуль:
cd ptdraft
python -m simulations.life.life
Таким образом, вы можете импортировать что угодно из nib.py, так как каталог ptdraft находится в пути.
Для меня самый короткий и мой любимый 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.
Тот же стиль, что и в прошлом ответе - но в меньшем количестве строк:P
import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)
файл возвращает местоположение, в котором вы работаете
В системе Linux вы можете создать программную ссылку из папки "life" на файл nib.py. Затем вы можете просто импортировать его как:
import nib
У меня есть решение специально для git-репозиториев.
Сначала я использовал и аналогичные решения. Это вызывает особые проблемы, если вы импортируете файлы, которые сами импортируют файлы с
sys.path.append('..')
.
Затем я решил всегда добавлять корневой каталог репозитория git. В одной строке это будет выглядеть так:
sys.path.append(git.Repo('.', search_parent_directories=True).working_tree_dir)
Или более подробно, как это:
import os
import sys
import git
def get_main_git_root(path):
main_repo_root_dir = git.Repo(path, search_parent_directories=True).working_tree_dir
return main_repo_root_dir
main_repo_root_dir = get_main_git_root('.')
sys.path.append(main_repo_root_dir)
Для исходного вопроса: в зависимости от корневого каталога репозитория импорт будет
import ptdraft.nib
или же
import nib
Мне кажется, что родительский модуль импортировать особо не нужно. Представим, что в nib.py у вас есть func1 () и data1, которые нужно использовать в life.py
nib.py
import simulations.life.life as life
def func1():
pass
data1 = {}
life.share(func1, data1)
life.py
func1 = data1 = None
def share(*args):
global func1, data1
func1, data1 = args
И теперь у вас есть доступ к func1 и данным в life.py. Конечно, вы должны быть осторожны, чтобы заполнить их в life.py, прежде чем пытаться их использовать,
Использование pathlib.Path
import sys
from pathlib import Path
sys.path.append(str(Path(f"{__file__}").parent.parent))
import my_module
Наша структура папок:
/myproject
project_using_ptdraft/
main.py
ptdraft/
__init__.py
nib.py
simulations/
__init__.py
life/
__init__.py
life.py
Насколько я понимаю, это представление, ориентированное на пакеты. Корень пакета - это самый верхний уровень, содержащий
__init__.py
Все файлы в пакете могут использовать абсолютные пути (относительно корня пакета) для импорта, например, в, у нас есть просто:
import ptdraft.nib
Однако для запуска
life.py
для разработки / тестирования пакетов вместо
python life.py
, нам нужно использовать:
cd ~/myproject
python -m ptdraft.simulations.life.life
Обратите внимание, что на этом этапе нам вообще не нужно было возиться с каким-либо путем.
Дальнейшая путаница возникает, когда мы завершаем пакет и хотим использовать его в сценарии драйвера, который обязательно находится за пределами
ptdraft
папка пакета, также известная как
project_using_ptdraft/main.py
, нам нужно будет возиться с путями:
import sys
sys.path.append("/myproject") # folder that contains ptdraft
import ptdraft
import ptdraft.simulations
и использовать
python main.py
чтобы запустить скрипт без проблем.
Полезные ссылки:
- https://chrisyeh96.github.io/2017/08/08/definitive-guide-python-imports.html#running-package-initialization-code
- /questions/5495365/importirovat-modul-kak-vnutri-odnogo-paketa-tak-i-snaruzhi-paketa-v-python-3/5495375#5495375
- /questions/32758410/kak-ispravit-popyitka-otnositelnogo-importa-v-ne-paket-dazhe-s-initpy/32758420#32758420
У меня возникла проблема, когда мне пришлось импортировать приложение Flask, в котором был импорт, который также требовал импорта файлов в отдельных папках. Это частично использует ответ Реми, но предположим, что у нас есть репозиторий, который выглядит так:
.
└── service
└── misc
└── categories.csv
└── test
└── app_test.py
app.py
pipeline.py
Затем перед импортом объекта приложения из app.py
файл, мы меняем каталог на один уровень выше, поэтому, когда мы импортируем приложение (которое импортирует pipeline.py
), мы также можем читать разные файлы, такие как файл csv.
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)
os.chdir('../')
from app import app
После импорта приложения Flask вы можете использовать os.chdir('./test')
так что ваш рабочий каталог не изменится.
Работа с библиотеками. Создайте библиотеку с именем nib, установите ее с помощью setup.py, пусть она будет находиться в пакетах сайтов, и ваши проблемы будут решены. Вам не нужно собирать все, что вы делаете в одной упаковке. Разбейте его на куски.
Другой способ, с минимальными усилиями, заключается в следующем:
import sys ; sys.path.insert(0,"path/to/module")
И затем добавьте это:
import Module
После удаления некоторых хаков системного пути я подумал, что было бы полезно добавить
Мое предпочтительное решение.
Примечание: это вызов фрейма — это не обязательно делать в коде .
Предполагая дерево,
project
└── pkg
└── test.py
Гдеtest.py
содержит
import sys, json; print(json.dumps(sys.path, indent=2))
Выполнение с использованием пути включает только каталог пакета
python pkg/test.py
[
"/project/pkg",
...
]
Но использование аргумента модуля включает каталог проекта
python -m pkg.test
[
"/project",
...
]
Теперь весь импорт может быть абсолютным из каталога проекта. Никаких дальнейших махинаций не требуется.
Я сделал эту библиотеку для этого.
https://github.com/fx-kirin/add_parent_path
# Just add parent path
add_parent_path(1)
# Append to syspath and delete when the exist of with statement.
with add_parent_path(1):
# Import modules in the parent path
pass
Это самое простое решение, которое работает для меня:
from ptdraft import nib