【问题标题】:How to make python package imports work in main?如何使 python 包导入在 main 中工作?
【发布时间】:2018-11-27 11:30:29
【问题描述】:

我有一个这样组织的简单应用:

.
`-- app
    |-- __init__.py (empty)
    |-- extensions.py (just defines foo())
    `-- server.py

server.py 看起来像这样:

from app.extensions import foo

python app/server.py 运行它当然不起作用,因为从命令行运行时 server.py 不在“应用程序中”。它的包名是__main__。我知道以python -m app.server 运行它可以工作,但由于各种原因我不能这样做(其中,我正在使用重新加载器,而python -m<foo>“有帮助”用完整的模块名称重写了 argv)。

我想我了解极其拜占庭式的 Python 包/模块导入内容,至少我已经阅读了很多。我认为解决此问题的标准(?)方法是将应用程序目录添加到 sys.path,例如sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))。但这对我来说似乎很奇怪,正确地做到这一点并非易事(请参阅有关此主题的其他类似 SO 问题)。似乎应该通过设置__name__ 和/或__package____path__ 或其他更隐藏的东西来告诉python“这是app.server”。

有没有办法像这样“欺骗”包导入系统?

【问题讨论】:

    标签: python python-import python-module


    【解决方案1】:

    不要去那里,没有必要开始纠结 Python 包结构。最好的方法是不要将脚本放入包中

    在包之外有一个单独的server.py 文件,并让它导入app.server 或其他模块来完成它的工作。这可以很简单:

    import sys
    from app.server import main
    
    sys.exit(main(sys.argv[1:]))
    

    除此之外,python -m 可能已经更新了sys.argv[0] 以指向完整的文件名(它基本上等于正在执行的模块中的__file__),但__spec__ module spec 对象包含原始包名。如果您必须使用python -m,请使用它并且需要将作为__main__运行的包的完整限定名称作为main属性:

    print(__spec__.name)
    

    所以对于package.main 模块,上面会打印package.main 而不是__main__

    $ mkdir package
    $ touch package/__init__.py
    $ echo 'if __name__ == "__main__": print("__spec__.name", __spec__.name)' > package/main.py
    $ python3 -m package.main
    package.main
    

    因此,如果您的重新加载器需要 sys.argv 包含 -m package.main,您可以更新 sys.argv 为:

    import sys
    
    sys.argv[:1] = ['-m', __spec__.name]
    

    【讨论】:

    • 谢谢,这是个好建议。当然不是我想听到的,但由于我无法让它发挥作用,很高兴知道我应该停止尝试。 :-/ 不会停止希望这个;它还可以使添加手动测试脚本更容易——我可以把它们放在有问题的模块旁边。
    猜你喜欢
    • 2012-10-16
    • 2019-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 2010-12-07
    相关资源
    最近更新 更多