【问题标题】:Hiding implementation files in a package在包中隐藏实现文件
【发布时间】:2012-01-07 21:48:57
【问题描述】:

我有一个名为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

因此,使用此项目布局 a 可以执行以下操作:

  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 代码使用模块的最佳方式是什么?

谢谢!

【问题讨论】:

    标签: python dynamic module package


    【解决方案1】:

    保留spellnum.py怎么样?

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

    【讨论】:

    • 谢谢。我选择了您的方法并稍作修改。我没有将if __name__ == '__main__' 块放在spellnum.py 中,而是将其提取到一个单独的脚本中。这清楚地表明spellnum 是一个用于 Python 代码的模块。提供命令行脚本只是为了方便。
    • 我也有同样的问题,但是除了 en.py 和 es.py,我还有一些 utils 模块。在 en.py 中,我从 utils 导入了一些函数。我可以正确安装我的包,但是当我导入它时,它说“没有名为 spelling.utils 的模块”有没有办法做到这一点?
    • published it as a new question,因为它略有不同
    【解决方案2】:

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

    【讨论】:

    • 谢谢,它是这样工作的。但是,我决定采用不同的方式并将main 部分提取到单独的脚本中。
    猜你喜欢
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 2013-07-21
    • 1970-01-01
    • 2011-01-19
    相关资源
    最近更新 更多