Как импортировать элементы модулей внутри пакета

Я разрабатываю пакет, который имеет структуру файла, похожую на следующее:

test.py
package/
    __init__.py
    foo_module.py
    example_module.py

Если я вызываю import package в test.py, я хочу, чтобы модуль пакета выглядел примерно так:

>>> vars(package)
mapping_proxy({foo: <function foo at 0x…}, {example: <function example at 0x…})

Другими словами, я хочу, чтобы члены всех модулей в package находились в пространстве имен package, и я не хочу, чтобы сами модули были в пространстве имен. package не является подпакетом.

Скажем, мои файлы выглядят так:

foo_module.py:

def foo(bar):
    return bar

example_module.py:

def example(arg):
    return foo(arg)

test.py:

print(example('derp'))

Как структурировать инструкции import в test.py, example_module.py и __init__.py для работы извне каталога пакета (т.е. test.py) и внутри самого пакета (например, foo_module.py и example_module.py )? Все, что я пытаюсь, дает Parent module '' not loaded, cannot perform relative import или ImportError: No module named 'module_name'.

Кроме того, в качестве побочного примечания (в соответствии с PEP 8): "Относительный импорт для внутрипакетов импортируется с высокой степенью обескуражения. Всегда используйте абсолютный путь пакета для всех импорта. Даже сейчас, когда PEP 328 полностью реализован в Python 2.5, его стиль явного относительного импорта активно обескуражен, абсолютный импорт более переносимый и обычно более читаемый."

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

Ответ 1

Ты сказал:

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

Я смог это сделать, адаптировав что-то, что я использовал в Python 2, чтобы автоматически импортировать подключаемые модули для работы в Python 3.

Вкратце, вот как это работает: пакет __init__.py файл импортирует все остальные файлы Python в тот же каталог пакетов, который не начинается с символа "_" (подчеркивание). После этого он добавляет имена в пространство имён импортированных модулей к модулю __init__ (который также является пространством имен пакетов). Примечание. Я должен был явно сделать модуль example_module import foo из foo_module.

Одним из важных аспектов такого поведения является понимание того, что он динамичен и не требует, чтобы имена модулей модулей были жестко закодированы в файле __init__.py. Конечно, это требует большего количества кода для выполнения, но также делает его очень универсальным и может работать практически с любым (одноуровневым) пакетом - поскольку он автоматически импортирует новые модули при их добавлении и прекращает импорт любых удаленных из каталог.

test.py

from package import *

print(example('derp'))

__ __ INIT ру:.

def _import_all_modules():
    """dynamically imports all modules in the package"""
    import traceback
    import os
    global __all__
    __all__ = []
    globals_, locals_ = globals(), locals()

    # dynamically import all the package modules
    for filename in os.listdir(__name__):
        # process all python files in directory that don't start with underscore
        # (which also keeps this module from importing itself)
        if filename[0] != '_' and filename.split('.')[-1] in ('py', 'pyw'):
            modulename = filename.split('.')[0]  # filename without extension
            package_module = '.'.join([__name__, modulename])
            try:
                module = __import__(package_module, globals_, locals_, [modulename])
            except:
                traceback.print_exc()
                raise
            for name in module.__dict__:
                if not name.startswith('_'):
                    globals_[name] = module.__dict__[name]
                    __all__.append(name)

_import_all_modules()

foo_module.py

def foo(bar):
    return bar

example_module.py

from package.foo_module import foo  # added

def example(arg):
    return foo(arg)

Ответ 2

Я думаю, вы можете получить нужные значения, не загромождая пространство имен, используя импорт стиля from module import name. Я думаю, что этот импорт будет работать для того, о чем вы просите:

Импорт для example_module.py:

from package.foo_module import foo

Импорт для __init__.py:

from package.foo_module import foo
from package.example_module import example

__all__ = [foo, example] # not strictly necessary, but makes clear what is public

Импорт для test.py:

from package import example

Обратите внимание, что это работает, только если вы используете test.py (или что-то еще на одном уровне иерархии пакетов). В противном случае вам нужно убедиться, что папка, содержащая package, находится в пути поиска модуля python (либо путем установки пакета где-то Python будет искать его, либо добавив соответствующую папку в sys.path).