Как импортировать класс Python, который находится в каталоге выше?

213

Я хочу наследовать от класса в файле, который находится в каталоге выше текущего.

Можно ли относительно импортировать этот файл?

user129975
источник

Ответы:

177

from ..subpkg2 import mod

Согласно документации по Python: внутри иерархии пакетов используйте две точки, как сказано в документе import import :

При указании модуля для импорта не нужно указывать абсолютное имя модуля. Когда модуль или пакет содержится в другом пакете, можно сделать относительный импорт в том же самом верхнем пакете, не упоминая имя пакета. Используя начальные точки в указанном модуле или пакете после, fromвы можете указать, как высоко пройти по текущей иерархии пакетов без указания точных имен. Одна начальная точка означает текущий пакет, в котором существует модуль, выполняющий импорт. Две точки означают один уровень пакета . Три точки выше двух уровней и т. Д. Итак, если вы выполняете from . import modиз модуля в pkgпакете, то в итоге вы импортируете pkg.mod. Если вы выполните . Спецификация для относительного импорта содержится вfrom ..subpkg2 import mod изнутри, pkg.subpkg1вы будете импортироватьpkg.subpkg2.mod PEP 328 .

PEP 328 имеет дело с абсолютным / относительным импортом.

гимель
источник
4
up1 = os.path.abspath ('..') sys.path.insert (0, up1)
rp.
Pep 328 показывает только Python-версию: 2.4, 2,5, 2.6. Версия 3 оставлена ​​для более знающих душ.
Гимел
22
Это вызывает ошибку ValueError: попытка относительного импорта за пределы пакета верхнего уровня
Карло
4
Результаты в ImportError: попытка относительного импорта без известного родительского пакета
Роткив
116
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Sepero
источник
1
это работает для меня. После добавления я могу напрямую импортировать родительские модули, не нужно использовать «..».
Эван Ху
8
это работает, грубо говоря, только если значение PWD приложения - его текущая directeroy, является дочерним элементом родителя. Таким образом, даже если это работает, многие виды системных изменений могут сместить его.
Флип
Это сработало и для меня при импорте модулей более высокого уровня. Я использую это с os.chdir ("..") для загрузки других файлов более высокого уровня.
Сурендра
Кажется, это вызывает ту же ошибку, что и приведенный выше ответ от Гимела.
Карло
81

Ответ @ gimel верен, если вы можете гарантировать иерархию пакетов, о которой он говорит. Если вы не можете - если ваша реальная потребность в том виде, в каком вы ее выразили, привязана исключительно к каталогам и не имеет никакого отношения к упаковке - тогда вам нужно поработать, __file__чтобы выяснить родительский каталог ( os.path.dirnameподойдет пара вызовов; -), затем (если этот каталог еще не sys.pathвключен) prepend временно вставляет указанный dir в самом начале sys.path, __import__снова удаляет указанный dir - грязная работа действительно, но, «когда вы должны, вы должны» (и Pyhon стремится никогда не мешайте программисту делать то, что должно быть сделано - как сказано в стандарте ISO C в разделе «Дух C» в предисловии! -).

Вот пример, который может работать для вас:

import sys
import os.path
sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

import module_in_parent_dir
Алекс Мартелли
источник
2
это может добавить каталог, который находится внутри пакета Python, чтобы sys.pathсделать один и тот же модуль доступным под разными именами и всеми соответствующими ошибками. autopath.py в pypy или _preamble.py в витой решают ее, используя критерии поиска, которые определяют пакет верхнего уровня при обходе каталогов вверх.
JFS
4
Возможно, вы захотите сделать что-то вроде sys.path.remove(pathYouJustAdded)после импорта, которое вам нужно, чтобы не сохранять этот новый путь.
Матиас
35

Импортировать модуль из каталога, который находится ровно на один уровень выше текущего каталога:

from .. import module
Sepero
источник
38
Я получил: попытка относительного импорта за пределы пакета верхнего уровня :(
RicardoE
25
Использование from .. import module Я получил ошибку ValueError: Попытка относительного импорта в не-пакет , следуя рекомендации
user3428154
4

Как загрузить модуль, который является каталогом

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

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

Python - это модульная система

Вот почему мы пишем import fooдля загрузки модуля «foo» из корневого пространства имен, вместо того, чтобы писать:

foo = dict();  # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh:  # please avoid doing this
    exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo)  # please avoid doing this

Python не связан с файловой системой

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

Благодаря отделению от файловой системы импорт может быть гибким, этот дизайн позволяет выполнять такие вещи, как импорт из архивных / zip-файлов, импорт одиночных файлов, кэширование байт-кода, расширения cffi, даже удаленная загрузка определения кода.

Так что, если импорт не связан с файловой системой, что означает «один каталог вверх»? Мы должны выбрать некоторую эвристику, но мы можем сделать это, например, при работе в пакете , уже была определена некоторая эвристика, которая делает относительный импорт похожим .fooи..foo работает в одном пакете. Прохладно!

Если вы искренне хотите соединить шаблоны загрузки исходного кода с файловой системой, вы можете сделать это. Вам придется выбрать собственную эвристику и использовать какую-то импортную технику, я рекомендую импортное importlib

Пример importlib в Python выглядит примерно так:

import importlib.util
import sys

# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'

foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)

foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton

упаковка

Официально представлен отличный пример проекта: https://github.com/pypa/sampleproject.

Пакет Python - это набор информации о вашем исходном коде, который может сообщить другим инструментам, как копировать ваш исходный код на другие компьютеры и как интегрировать ваш исходный код в путь этой системы, чтобы он import fooработал на других компьютерах (независимо от интерпретатора, операционная система хоста и т. д.)

Структура каталогов

Позволяет иметь имя пакета fooв некотором каталоге (предпочтительно пустой каталог).

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

Я предпочитаю создавать setup.pyкак брат foo.py, так как это упрощает написание файла setup.py, однако вы можете написать конфигурацию для изменения / перенаправления всего, что setuptools делает по умолчанию, если хотите; например, размещение foo.pyв каталоге "src /" несколько популярно, здесь не рассматривается.

some_directory/
    foo.py
    setup.py

,

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)

,

python3 -m pip install --editable ./  # or path/to/some_directory/

«редактируемый» aka -eеще раз перенаправит импортирующий механизм для загрузки исходных файлов в этот каталог, вместо этого копируя текущие точные файлы в библиотеку установочной среды. Это также может вызвать поведенческие различия на компьютере разработчика, обязательно протестируйте свой код! Есть и другие инструменты, кроме pip, однако я бы рекомендовал pip быть вводным :)

Мне также нравится делать foo«пакет» (директория, содержащая __init__.py) вместо модуля (один файл «.py»), и «пакеты», и «модули» могут быть загружены в корневое пространство имен, модули допускают вложенные пространства имен, что полезно, если мы хотим иметь импорт «на одну директорию вверх».

some_directory/
    foo/
        __init__.py
    setup.py

,

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

Мне также нравится делать a foo/__main__.py, это позволяет python выполнять пакет как модуль, например python3 -m foo, выполнять foo/__main__.pyкак __main__.

some_directory/
    foo/
        __init__.py
        __main__.py  # `if __name__ == "__main__":`  lives here, `def main():` too!
    setup.py

,

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
    ...
    entry_points={
        'console_scripts': [
            # "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
            'foo=foo.__main__:main',
        ]
    },
)

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

some_directory/
    bar.py           # `import bar`
    foo/
        __init__.py  # `import foo`
        __main__.py
        baz.py       # `import foo.baz
        spam/           
            __init__.py  # `import foo.spam`
            eggs.py      # `import foo.spam.eggs`
    setup.py

setup.py условно содержит метаданные о исходном коде, такие как:

  • какие зависимости необходимы для установки с именем "install_requires"
  • какое имя следует использовать для управления пакетами (установить / удалить «имя»), я предлагаю, чтобы это совпадало с вашим основным именем пакета python в нашем случае foo , хотя замена дефисов подчеркиванием является популярным
  • лицензионная информация
  • теги зрелости (альфа / бета / и т. д.),
  • теги аудитории (для разработчиков, для машинного обучения и т. д.),
  • содержание одностраничной документации (например, README),
  • имена оболочки (имена, которые вы вводите в пользовательской оболочке, например, bash, или имена, которые вы находите в графической оболочке пользователя, например, в меню «Пуск»),
  • список модулей Python, которые этот пакет установит (и удалит)
  • точка входа "запустить тесты" python ./setup.py test

Она очень обширна, она может даже компилировать расширения c на лету, если на машине разработчика устанавливается исходный модуль. В качестве примера на каждый день я рекомендую файл setup.py репозитория образцов PYPA

Если вы выпускаете артефакт сборки, например, копию кода, предназначенного для запуска практически идентичных компьютеров, файл require.txt - это популярный способ получения точной информации о зависимостях, где «install_requires» - это хороший способ собрать минимум и максимально совместимые версии. Однако, учитывая, что целевые машины в любом случае почти идентичны, я настоятельно рекомендую создать тарбол с полным префиксом python. Это может быть сложно, слишком подробно, чтобы попасть сюда. Проверьте pip install's --targetвариант, или virtualenv aka venv для лидов.

вернуться к примеру

Как импортировать файл на один каталог вверх:

Из foo / spam / eggs.py, если мы хотим получить код из foo / baz, мы можем запросить его по абсолютному пространству имен:

import foo.baz

Если бы мы хотели зарезервировать возможность в будущем переместить eggs.py в какой-либо другой каталог с другой относительной bazреализацией, мы могли бы использовать относительный импорт, например:

import ..baz
ThorSummoner
источник
-5

Python - это модульная система

Python не полагается на файловую систему

Чтобы надежно загрузить код Python, поместите этот код в модуль, и этот модуль должен быть установлен в библиотеке Python.

Установленные модули всегда можно загрузить из пространства имен верхнего уровня с помощью import <name>


Официально представлен отличный пример проекта: https://github.com/pypa/sampleproject.

По сути, вы можете иметь структуру каталогов следующим образом:

the_foo_project/
    setup.py  

    bar.py           # `import bar`
    foo/
      __init__.py    # `import foo`

      baz.py         # `import foo.baz`

      faz/           # `import foo.faz`
        __init__.py
        daz.py       # `import foo.faz.daz` ... etc.

,

Обязательно объявить setuptools.setup()по прибытию setup.py,

официальный пример: https://github.com/pypa/sampleproject/blob/master/setup.py

В нашем случае мы, вероятно, хотим экспортировать, bar.pyи foo/__init__.pyмой краткий пример:

setup.py

#!/usr/bin/env python3

import setuptools

setuptools.setup(
    ...
    py_modules=['bar'],
    packages=['foo'],
    ...
    entry_points={}, 
        # Note, any changes to your setup.py, like adding to `packages`, or
        # changing `entry_points` will require the module to be reinstalled;
        # `python3 -m pip install --upgrade --editable ./the_foo_project
)

,

Теперь мы можем установить наш модуль в библиотеку Python; с помощью pip вы можете установить the_foo_projectвашу библиотеку python в режиме редактирования, чтобы мы могли работать с ней в режиме реального времени

python3 -m pip install --editable=./the_foo_project

# if you get a permission error, you can always use 
# `pip ... --user` to install in your user python library

,

Теперь из любого контекста Python мы можем загрузить наши общие py_modules и пакеты

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))
ThorSummoner
источник
2
Следует отметить, что я всегда устанавливаю свой модуль, пока работаю над ним pip install --edit foo, почти всегда внутри virtualenv. Я почти никогда не пишу модуль, который не предназначен для установки. если я что-то неправильно понимаю, я хотел бы знать.
ThorSummoner
Я также должен отметить, что использование набора тестов venv-auto-creation, таких как tox, очень полезно, так как editableссылки на egg и установленные модули python не совсем одинаковы во всех отношениях; например, добавление нового пространства имен в редактируемый модуль будет найдено в вашем пути поиска, но если это не экспортируется в ваш файл setup.py, он не будет упакован / установлен! Проверьте ваш вариант использования :)
ThorSummoner