假设我有以下简单的项目结构。
project/
package/
__init__.py
__main__.py
module.py
requirements.txt
README.md
在对谷歌进行了一些研究后,我试图让它反映出一个非常简单的控制台应用程序的一般最佳实践(但不是简单地拥有一个脚本那么简单(。假设__init__.py
只包含print("Hello from __init__.py")
,而module.py
包含类似的语句。
我应该如何在__main__.py
内部进行导入,然后我应该如何运行我的项目?
让我们首先假设__main__.py
看起来很简单:
import module
print("Hello from __main__.py")
如果我用简单的命令python package
运行我的项目,我会得到以下输出:
Hello from module.py
Hello from __main__.py
可以看出,__init__.py
没有运行。我想这是因为我正在将我的项目包作为脚本运行。如果我用命令python -m package
将其作为一个模块运行,我会得到以下输出:
Hello from __init__.py
Traceback (most recent call last):
File "C:UsersMY_USERAppDataLocalProgramsPythonPython38librunpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:UsersMY_USERAppDataLocalProgramsPythonPython38librunpy.py", line 87, in _run_code
exec(code, run_globals)
File "C:UsersMY_USERPATHTOPROJECTprojectpackage__main__.py", line 1, in <module>
import module
ModuleNotFoundError: No module named 'module'
如果我将__main__.py
中的第一行更改为import package.module
并再次运行python -m package
,我会得到以下输出:
Hello from __init__.py
Hello from module.py
Hello from __main__.py
太棒了!现在,当我将项目的包作为一个模块运行时,似乎一切都能正常运行。现在,如果我再次尝试python package
并将其作为脚本运行,该怎么办?
Traceback (most recent call last):
File "C:UsersMY_USERAppDataLocalProgramsPythonPython38librunpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:UsersMY_USERAppDataLocalProgramsPythonPython38librunpy.py", line 87, in _run_code
exec(code, run_globals)
File "package__main__.py", line 1, in <module>
import package.module
ModuleNotFoundError: No module named 'package'
好吧。所以,如果我错了,请纠正我,但我似乎有两个选择。我可以在包中编写导入以将其作为脚本运行,也可以作为模块运行,但不能两者兼而有之。哪一个更好,如果一个确实更可取,为什么?您什么时候使用命令python package
与python -m package
,为什么?在一个我可能不理解的简单项目中编写导入是否有一些通用规则?我是不是错过了其他一些基本的东西?
总之:在这种情况下,什么是最佳实践,为什么是最佳实践?您何时将项目设置为替代方法(python package
与python -m package
(?
分发可执行python代码的最常见方法是将其打包到可安装的.wheel
文件中。如果它只是一个文件,您也可以直接分发它。但是,一旦您获得两个文件,就会遇到您遇到的确切导入问题,在这一点上,您需要一些元数据来为导入(例如,在您的情况下是package.module
(、脚本代码的入口点、第三方依赖关系。。。所有这些都是通过";使得代码是可安装的";。
如果你喜欢技术文档,你可以通过阅读python打包机构(PyPA(的本教程和本教程来了解这到底意味着什么。
不过,要开始您的项目,您缺少的是一个setup.py
文件,该文件将包含安装说明和脚本入口点,以提供从包中运行可执行代码的方法:
from setuptools import setup
with open("requirements.txt") as f:
requirements = [line.strip() for line in f.readlines()]
setup(
# obligatory, just the name of the package. you called it "package" in your
# example so that's the name here
name="package",
# obligatory, when it's done you can give it a 1.0
version="0.1",
# point the installer to the module (read, folder) that contains your code,
# by convention usually the same as the package name
packages=["package"],
# if there are dependencies, specify them here. actually you can delete the
# requirements.txt and just paste the content here, but this here will also work
install_requires=requirements,
# point the installer to the function that will run your executable code.
# the key name has got to be 'console_script', the value content is up
# to you, with its interpretation being:
# 'package_command' -> the name that you can call your code by
# 'package.__main__' -> the path to the file that you want to call
# 'test' -> the actual function that contains the code
entry_points={'console_scripts': ['package_command=package.__main__:test']}
)
将此文件添加为project/setup.py
后,您需要记住以下几点:
- 将脚本代码(例如
print('hello world)'
(放入__main__.py
文件中名为test
的函数中[1] - 运行
pip install -e .
在本地安装程序包[2] - 通过在命令行上运行
package_command
来执行脚本代码-无论是python package
还是python -m package
都不是运行可安装python脚本的最佳实践
[1]将一个简单函数绑定到脚本入口点是最基本的。你可能不想重新发明帮助文本、论点解析/验证等东西。。。因此,如果您真的想编写一个cli应用程序,您可能需要研究click
之类的东西来处理这些乏味的东西
[2]这将执行开发安装,这在开发过程中很方便,因为这意味着你可以在工作时测试行为。如果你想分发代码,你应该运行pip wheel .
(之前可能需要运行pip install wheel
(。这将创建一个轮子文件,您可以将其提供给客户端,以便他们可以使用pip install package-0.1-py3-none-any.whl
手动安装,之后package_command
也将在其系统上可用