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

В приведенном ниже иерархии есть удобный и универсальный способ ссылаться на top_package, используя общий термин во всех.py файлах ниже? Я хотел бы иметь последовательный способ импорта других модулей, так что даже когда "top_package" изменяет имя, ничего не сломается.

Я не сторонник использования относительного импорта типа "..level_one_a", поскольку относительный путь будет отличаться от каждого файла python ниже. Я ищу способ, которым:

  1. Каждый файл python может иметь тот же оператор импорта для того же модуля в пакете.
  2. Развязывающая ссылка на "top_package" в любом.py файле внутри пакета, поэтому любое имя "top_package" изменяется, ничего не сломается.

    top_package/
      __init__.py
      level_one_a/
        __init__.py
        my_lib.py
        level_two/
          __init__.py
          hello_world.py
      level_one_b/
        __init__.py
        my_lib.py
      main.py
    

Ответ 1

Это должно сделать работу:

top_package = __import__(__name__.split('.')[0])

Трюк здесь заключается в том, что для каждого модуля переменная __name__ содержит полный путь к модулю, разделенному точками, например, top_package.level_one_a.my_lib. Следовательно, если вы хотите получить имя верхнего пакета, вам просто нужно получить первый компонент пути и импортировать его с помощью __import__.

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

Ответ 2

Поместите свой пакет и main скрипт в каталог внешнего контейнера, например:

container/
    main.py
    top_package/
        __init__.py
        level_one_a/
            __init__.py
            my_lib.py
            level_two/
                __init__.py
                hello_world.py
        level_one_b/
            __init__.py
            my_lib.py

Когда main.py запущен, его родительский каталог (container) будет автоматически добавлен в начало sys.path. И поскольку top_package теперь находится в одном каталоге, его можно импортировать из любого места в дереве пакетов.

Итак, hello_world.py может импортировать level_one_b/my_lib.py следующим образом:

from top_package.level_one_b import my_lib

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

Но учтите, что в вашем исходном примере top_package может легко функционировать как сам каталог контейнера. Все, что вам нужно сделать, это удалить top_package/__init__.py, и вы top_package/__init__.py с такой же компоновкой.

Предыдущая инструкция импорта затем изменится на:

from level_one_b import my_lib

и вы могли бы свободно переименовать top_package бы вы ни хотели.

Ответ 3

Вы можете использовать комбинацию функции __import__() и атрибута __path__ пакета.

Например, предположим, что вы хотите импортировать <whatever>.level_one_a.level_two.hello_world из другого места в пакете. Вы могли бы сделать что-то вроде этого:

import os
_temp = __import__(__path__[0].split(os.sep)[0] + ".level_one_a.level_two.hello_world")
my_hello_world = _temp.level_one_a.level_two.hello_world

Этот код не зависит от имени пакета верхнего уровня и может использоваться в любом месте пакета. Это также довольно уродливо.

Ответ 4

Я считаю, что №2 невозможно без использования относительного импорта или именованного пакета. Вы должны указать, какой модуль нужно импортировать, явно указав его имя или используя относительный импорт. иначе как интерпретатор узнает, чего вы хотите?

Если вы top_level/ приложение на один уровень выше top_level/ и import top_leve l, вы можете ссылаться на top_level.* Из любого места в пакете top_level.

(Я могу показать вам пример из программного обеспечения, над которым я работаю: http://github.com/toddself/beerlog/)

Ответ 5

Это работает в библиотечном модуле:

import __main__ as main_package
TOP_PACKAGE = main_package.__package__.split('.')[0]