Скрытие файлов реализации в пакете - PullRequest
3 голосов
/ 07 января 2012

У меня есть модуль с именем spellnum. Его можно использовать в качестве утилиты командной строки (он имеет блок if __name__ == '__main__':) или импортировать как стандартный модуль Python.

Модуль определяет класс с именем Speller, который выглядит следующим образом:

class Speller(object):
    def __init__(self, lang="en"):
        module = __import__("spelling_" + lang)
        # use module's contents...

Как видите, конструктор класса загружает другие модули во время выполнения. Эти модули (spelling_en.py, spelling_es.py и т. Д.) Расположены в том же каталоге, что и сам spellnum.py.

Помимо spellnum.py, существуют другие файлы с функциями и классами утилит. Я хотел бы скрыть эти файлы, так как я не хочу показывать их пользователю, и поскольку плохая идея - загрязнять каталог lib в Python случайными файлами. Единственный способ добиться этого, о котором я знаю, - это создать пакет.


Я придумал этот макет для проекта (вдохновленный этим замечательным учебником ):

spellnum/                # project root
    spellnum/            # package root
        __init__.py
        spellnum.py
        spelling_en.py
        spelling_es.py
        squash.py
        # ... some other private files
    test/
        test_spellnum.py
    example.py

Файл __init__.py содержит одну строку:

from spellnum import Speller

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

class Speller(object):
    def __init__(self, lang="en"):
        spelling_mod = "spelling_" + lang
        package = __import__("spellnum", fromlist=[spelling_mod])
        module = getattr(package, spelling_mod)
        # use module as usual

Итак, с этим макетом проекта можно сделать следующее:

  1. Успешно import spellnum внутри example.py и используйте его как простой модуль:

    # an excerpt from the example.py file
    import spellnum
    
    speller = spellnum.Speller(es)
    # ...
    
  2. import spellnum в тестах и ​​запустите эти тесты из корня проекта следующим образом:

    $ PYTHONPATH="`pwd`:$PYTHONPATH" python test/test_spellnum.py
    

проблема

Я не могу выполнить spellnum.py напрямую с новым макетом. Когда я пытаюсь, это показывает следующую ошибку:

Traceback (most recent call last):
  ...
  File "spellnum/spellnum.py", line 23, in __init__
    module = getattr(package, spelling_mod)
AttributeError: 'module' object has no attribute 'spelling_en'

Вопрос

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

Спасибо!

Ответы [ 2 ]

2 голосов
/ 07 января 2012

Как насчет хранения spellnum.py?

spellnum.py
spelling/
  __init__.py
  en.py
  es.py
1 голос
/ 07 января 2012

Ваша проблема в том, что пакет называется так же, как python-файл, который вы хотите выполнить, поэтому при импорте

from spellnum import spellnum_en

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

def __init__(self, lang="en"):
    mod = "spellnum_" + lang
    module = None
    if __name__ == '__main__':
        module = __import__(mod)
    else:
        package = getattr(__import__("spellnum", fromlist=[mod]), mod)
...