python - 也可以作为命令行脚本运行的包结构

标签 python python-3.x command-line argparse python-packaging

我用the 'standard' minimal structure写了一个包.它看起来像这样:

my_package/
    my_package/
        __init__.py
    setup.py

__init__.py 包含一个类,因此可以像预期的那样简单地导入和使用。

但是,该代码确实适合以命令行方式使用,例如

python my_package --arg1 "I like bananas."

起初,我只是有一个 if __name__ == '__main__' 检查 __init__ 然后使用 argparse。这有效,但它并不漂亮,因为这意味着您将像这样从命令行调用它:

python my_package/__init__.py --arg1 "I like bananas."

根据我的阅读,这是一个 __main__.py 文件的来源,该文件将作为文件夹内的默认脚本执行(类似于 index.html网站上的文件)。我的想法是然后简单地导入 __init__.py,运行 argparse 并将参数提供给类构造函数。像这样:

import argparse
from __init__ import MyClass

parser = argparse.ArgumentParser()
parser.add_argument("--arg1", help="Some dummy value")

args = parser.parse_args()
my_class = MyClass(**vars(args))
my_class.do_stuff()

类似的包应该是这样构建的,还是有更好的方法?


上面的工作但是 PyCharm 告诉我 __main__.py __init__ 是一个未解析的引用。与该导入行上的 MyClass 相同。当我使用 .__init__ 代替(带点)时,警告消失但代码不再工作,给我一个 ImportError: attempted relative import with no known parent package.

最佳答案

我想向您推荐一个不同的结构:

my_package/
    my_package/
        __init__.py
        cli_scripts.py
    setup.py

假设您的 __init__.py 看起来像这样(作为旁注,我建议将其中定义的类移动到一个单独的文件中,然后简单地将该文件导入 __init__):

class Test(object):

    def __init__(self, a)
        self.a = a

    def __call__(self):
        print(self.a)

现在包内有一个额外的文件,它利用了您在包中实现的内容,我们称它为 cli_scripts.py:

import argparse

from my_package import Test


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("a", type=int, help="Just an example argument")
    return parser.parse_args()

def demonstration():
    args = parse_args()
    t = Test(a=args.a)
    t()

我现在的建议是利用 console_scripts entry pointsetup.py 中,现在看起来像这样:

from setuptools import setup

setup(
    name='testpy',
    version='0.1',
    description='Just an example',
    author='RunOrVeith',
    author_email='xyz@mailinator.com',
    packages=["my_package"],
    entry_points={
        "console_scripts": ["mypkg=my_package.cli_scripts:demonstration"]},
    install_requires=[],
)

现在,当您在顶级 my_package 文件夹中运行 pip install . 时,您的包将被安装。 entry_points 自动为您生成一个可执行文件,您现在可以使用您在 setup.py 中给它的名称调用它,在本例中为 mypkg .这意味着您现在可以运行

mypkg 5 这将调用 demonstration()

这样:

  • 你不需要处理任何__main__文件
  • 你可以给你的脚本一个有意义的名字
  • 你不需要关心你的脚本是否可执行,或者指定用python运行脚本
  • 你可以在列表 entry_points
  • 中拥有任意数量的这些
  • 它迫使你编写你也可以从其他模块调用的函数, 而不是 __main__

我认为这很干净。 您可以在 this blog 中阅读有关 entry_points 的更多信息,它有更多的功能! 如果你想使用 __main__ 中指定的代码而不是这种 cli_scripts 方法,你可以看看 this question .

关于python - 也可以作为命令行脚本运行的包结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53865866/

相关文章:

java - 用于调用 java 类的命令行的自定义命令

python - 我可以用 C 重写昂贵的 pygame 函数吗?

java - 如何更好地跟踪 Java 中的索引(相对于 Python)

python - 加速处理500万行坐标数据

python - 如何将多个 bool 列转换为带有列标题和 NaN 的一列?

python - 如何扩展元组项作为参数 os 系统调用?

python - 如何减少 TensorFlow 循环中的内存消耗?

python - lru_cache 转储到文件中并再次加载回内存中

python - 我可以使用 Python SDK 对 Firebase Firestore 子项进行排序吗?

javascript - 批量修复 JSHints 在项目中发现的缺失分号