隐藏包中的实现文件

时间:2012-01-07 13:20:31

标签: python dynamic module package

我有一个名为spellnum的模块。它可以用作命令行实用程序(它具有if __name__ == '__main__':块),也可以像标准Python模块一样导入。

该模块定义了一个名为Speller的类,如下所示:

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

如您所见,类构造函数在运行时加载其他模块。这些模块(spelling_en.pyspelling_es.py等)与spellnum.py本身位于同一目录中。

spellnum.py外,还有其他具有实用功能和类的文件。我想隐藏这些文件,因为我不想将它们暴露给用户,因为用随机文件污染Python的lib目录是个坏主意。我知道实现这一目标的唯一方法是创建一个包。


我已经为项目提出了这个布局(灵感来自这个伟大的tutorial):

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. 测试中的
  3. import spellnum并从项目根目录运行这些测试:

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

    我无法直接使用新布局执行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 个答案:

答案 0 :(得分:2)

如何保持spellnum.py

spellnum.py
spelling/
  __init__.py
  en.py
  es.py

答案 1 :(得分:1)

您的问题是,该包的调用与您要执行的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)