我应该在 __main__.py 中使用什么形式的导入,然后我应该如何运行项目?

Aar*_*oin 6 python python-module python-import python-packaging

假设我有以下简单的项目结构。

project/
    package/
        __init__.py
        __main__.py
        module.py
    requirements.txt
    README.md
Run Code Online (Sandbox Code Playgroud)

在对谷歌做了一些研究之后,我试图让它反映一个非常简单的控制台应用程序的一般最佳实践(但不像简单的只有一个脚本那么简单)。假设__init__.pycontains simpleprint("Hello from __init__.py")并且module.py包含一个类似的语句。

我应该如何在 内部进行导入__main__.py,然后我应该如何运行我的项目?

首先让我们说它__main__.py看起来很简单:

import module
print("Hello from __main__.py")
Run Code Online (Sandbox Code Playgroud)

如果我使用简单的命令运行我的项目python package,我会得到以下输出:

Hello from module.py
Hello from __main__.py
Run Code Online (Sandbox Code Playgroud)

可以看出,__init__.py没有跑。我认为这是因为我将我的项目包作为脚本运行。如果我改为使用命令将其作为模块运行,则会python -m package得到以下输出:

Hello from __init__.py
Traceback (most recent call last):
  File "C:\Users\MY_USER\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\MY_USER\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\MY_USER\PATH\TO\PROJECT\project\package\__main__.py", line 1, in <module>
    import module
ModuleNotFoundError: No module named 'module'
Run Code Online (Sandbox Code Playgroud)

如果我将第一行更改__main__.pyimport package.modulepython -m package再次运行,则会得到以下输出:

Hello from __init__.py
Hello from module.py
Hello from __main__.py
Run Code Online (Sandbox Code Playgroud)

伟大的!将我的项目包作为模块运行时,现在似乎一切正常。现在,如果我再试python package一次并将其作为脚本运行会怎样?

Traceback (most recent call last):
  File "C:\Users\MY_USER\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\MY_USER\AppData\Local\Programs\Python\Python38\lib\runpy.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'
Run Code Online (Sandbox Code Playgroud)

好吧。所以如果我错了,请纠正我,但似乎我有两个选择。我可以在我的包中编写导入以将其作为脚本运行或作为模块运行,但不能同时运行。如果确实更可取,哪个更好,为什么?你什么时候会使用命令python packagevs python -m package,为什么?在一个我可能不理解的简单项目中编写导入是否有一些一般规则?我错过了其他基本的东西吗?

总结:这种情况下的最佳实践是什么,为什么是最佳实践,以及您何时将项目设置为替代方法(python packagevs python -m package)?

Arn*_*rne 4

分发可执行 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']}
)
Run Code Online (Sandbox Code Playgroud)

添加此文件后project/setup.py,您需要记住以下事项:

  • 将您的脚本代码(例如)放入文件中print('hello world)'调用的函数中[1]test__main__.py
  • 运行pip install -e .以在本地安装您的软件包[2]
  • package_command通过在命令行上运行来执行脚本代码-运行可安装的 python 脚本既不是python package也不是最佳实践python -m package

[1]将简单函数绑定到脚本入口点是最基本的。您可能不想重新发明诸如帮助文本、参数解析/验证之类的东西......所以如果您真的想编写一个 cli 应用程序,您可能需要研究一些类似click处理繁琐事情的东西你。

[2]这会执行开发安装,这在开发过程中很方便,因为这意味着您可以在工作时测试行为。如果您想分发代码,则需要运行pip wheel .(可能需要pip install wheel先运行)。这将创建一个 Wheel 文件,您可以将其提供给客户,以便他们可以使用 手动安装它pip install package-0.1-py3-none-any.whl,之后package_command也可以在他们的系统上使用。