在我的程序包文件中放一个main函数是不是不好?
对子模块来说,定义main
函数不一定是坏事,但如果您希望记录器名称不是"__main__"
,那么您将希望避免执行子模块的值为top-level code.不要试图对抗或摆弄__main__
的机器,这部分语言是僵化的,不太可能因为Guido considers executing submodules directly as scripts an anti-pattern而改变.
相反,您希望顶级代码环境是一个包装器脚本,它导入main
并执行它.这样,logger = logging.getLogger(__name__)
的正常模式将按预期工作,为您的子包创建适当的命名记录器层次 struct .日志(log)记录树层次 struct 很重要,以便可以递归地配置日志(log)记录处理程序,并且记录器可以传播到单个 node (即根记录器).
有一个名为Entry Points的Python打包特性,可用于自动生成这些包装器脚本.我将演示how to define them with setuptools,但现在所有其他主要构建系统都支持它们,因为它们是在PEP 621 – Storing project metadata in pyproject.toml年前指定的.
在您的子模块中,您将拥有类似于(简化)的内容:
# package_name/functionality_1.py
import logging
log = logging.getLogger(__name__)
def main():
logging.basicConfig(format="%(name)s:%(message)s", level=logging.INFO)
log.info("hello")
在您的pyproject.toml
文件中,添加一个包含以下内容的部分:
[project.scripts]
functionality-1 = "package_name.functionality_1:main"
functionality-2 = "package_name.functionality_2:main"
安装程序(通常为pip)将自动生成包装脚本并将它们放在$路径上.这些脚本是您应该调用的,而不是直接作为__main__
执行子模块,即,而不是使用:
python3 -m package_name.functionality_1
python3 -m package_name.functionality_2
您将拨打:
functionality-1
functionality-2
您可以从源代码中删除所有if __name__ == "__main__:
个块,它们不是必需的.对于开发/测试,使用pip install -e .
将包安装为editable(正是此安装命令将生成包装器脚本).
如果您愿意,可以查看包装器脚本的内容,看看它是如何工作的.你会在sysconfig.get_path("scripts")
指定的位置找到它们.它们将设置可执行模式位,如果您在MacOS/Linux上,它们将有一个#!Shebang指向相应的Python环境(即用于安装的运行时).正是这些脚本作为顶级代码执行,它们只会从您的包代码中导入main
函数(S)并调用它们.