Как сослаться на модуль верхнего уровня в Python внутри пакета? - PullRequest
18 голосов
/ 03 января 2012

В приведенной ниже иерархии есть ли удобный и универсальный способ ссылки на 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
    

Ответы [ 5 ]

13 голосов
/ 03 января 2012

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

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

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

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

7 голосов
/ 03 января 2012

Поместите ваш пакет и скрипт 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, и вы останетесь в том же положении.

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

from level_one_b import my_lib

и вы можете переименовать top_package так, как пожелаете.

1 голос
/ 03 января 2012

Вы можете использовать комбинацию функции __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

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

0 голосов
/ 11 марта 2016

Это работает из библиотечного модуля:

import __main__ as main_package
TOP_PACKAGE = main_package.__package__.split('.')[0]
0 голосов
/ 03 января 2012

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

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

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

...