Как сохранить имена подмодулей вне пространства имен пакета Python? - PullRequest
12 голосов
/ 10 марта 2011

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

mypackage/
    __init__.py
    a.py
    b.py
    c.py
    d.py

Чтобы все равно получить нужный интерфейс, я определяю файл __init__.py для пакета, который импортирует все открытые символы из a, b, c и d:

from a import func_a1, func_a2, ClassA1, ClassA2
from b import func_b1, func_b2, ClassB1, ClassB2
from c import func_c1, func_c2, ClassC1, ClassC2
from d import func_d1, func_d2, ClassD1, ClassD2

Если я импортирую пакет, используя

import mypackage

пространство имен пакета также содержит символы a, b, c и d. Эти имена являются деталями реализации, а не частью моего интерфейса. Я не хочу, чтобы они появлялись как «публичные» символы. Какой лучший способ избавиться от них?

Варианты, которые я рассмотрел:

  1. Используйте один модуль вместо пакета. Интерфейс будет хорошо выглядеть, но реализация станет менее понятной, чем сейчас.

  2. Добавить строку

    del a, b, c, d
    

    до конца __init__.py. Работает нормально, но похоже на взлом. (Например, вы больше не можете import __init__, что работает без этой строки.)

  3. Переименуйте a, b, c и d в _a, _b, _c и _d. Теперь они включены в пространство имен mypackage как "частные" символы, с которыми я в порядке, но немного странно, что все мои имена файлов начинаются с подчеркивания (на самом деле, есть конечно больше четырех подмодулей).

Есть предложения получше? Или мысли о том, какой вариант предпочесть?

Или я просто к аналу и мне все равно?

Ответы [ 4 ]

12 голосов
/ 10 ноября 2011

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

Например, если вы посмотрите в ctypes, вы увидите

__init__.py
==================================================
"""create and manipulate C data types in Python"""

import os as _os, sys as _sys

__version__ = "1.1.0"

from _ctypes import Union, Structure, Array
from _ctypes import _Pointer
from _ctypes import CFuncPtr as _CFuncPtr
...

Как видите, даже os и sys стали подробностями реализации в этом файле.

5 голосов
/ 10 марта 2011

Если вы действительно хотите удалить имена из пространства имен, вы можете просто использовать для них оператор del, и они исчезнут, как ветер.

0 голосов
/ 04 апреля 2011

Вот решение, вдохновленное однофункциональными модулями Javascript:

def __init__module():
    from os import path

    def _module_export_1():
        return path.abspath('../foo')

    def _module_export_2():
        return path.relpath('foo/bar', 'foo')

    g = globals()
    g['module_export_1'] = _module_export_1
    g['module_export_2'] = _module_export_2

__init__module()

Хотя модуль должен импортировать «путь» из ОС, «путь» не загрязняет пространство имен модуля. Единственное несоответствие в пространстве имен модуля - это __init_module (), который четко помечен как закрытый префиксом двойного подчеркивания.

Другой вариант - импортировать необходимые модули вверху каждой функции, а не вверху вашего модуля. После первого импорта модуля последующий импорт - это просто поиск в словаре sys.modules.

Но я согласен с другими комментаторами здесь - соглашение Python не о том, чтобы беспокоиться о загрязнении пространства имен модулей, а просто показывает пользователям вашего модуля, какие части пространства имен являются вашим общедоступным API, а какие - внутренними.

0 голосов
/ 10 марта 2011

С http://docs.python.org/tutorial/modules.html:

Оператор импорта использует следующее соглашение: если код пакета __init__.py определяет список с именем __all__, он принимается за список имен модулей, который должен бытьимпортируется, когда из пакета импортируется *.

В вашем mypackage/__init__.py попробуйте добавить это:

# add this line, replace "..." with the rest of the definitions
# you want made public
__all__ = ['func_a1', 'func_a2', 'ClassA1', 'ClassA2', ...]

from a import func_a1, func_a2, ClassA1, ClassA2
from b import func_b1, func_b2, ClassB1, ClassB2
from c import func_c1, func_c2, ClassC1, ClassC2
from d import func_d1, func_d2, ClassD1, ClassD2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...