【问题标题】:Convert __main__ function from Python script intended for command line into a callable function将用于命令行的 Python 脚本中的 __main__ 函数转换为可调用函数
【发布时间】:2018-12-18 23:29:05
【问题描述】:

我正在使用某人的 GitHub 代码,该代码旨在从命令行调用,如下所示:

> python this_script.py -u <username> -p <password> -i <id_num> ...

这会在this_script.py 中以这种方式解析输入后生成一个输出文本文件:

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='get data', add_help=False, usage='this_script.py -u username -p password [options]')
    parser.add_argument('-u', metavar='<STR>', type=str, help='username')
    parser.add_argument('-p', metavar='<STR>', type=str, help='password')
    parser.add_argument('-i', metavar='<STR>', nargs='+', type=str, help='List of IDs')
    ...

我想在我自己的代码集中使用其中的许多文本文件,因此将其从命令行脚本转换为可调用函数会更方便,语法如下:

def this_script(password, username, *args):
    ...

有没有一种简单的方法可以做到这一点,而不必过多地处理当前定义的 __main__ 块的内部工作?

【问题讨论】:

  • 我在您的示例中没有看到任何名为 main 的函数?这完全取决于代码......通常你可以将所有内容放入一个函数中并完成,但如果代码依赖于 globals&stuff,则情况并非如此。以及如何重构完全取决于具体的代码。你所说的问题太宽泛了。你应该提供一个具体的例子,你的尝试以及它有什么问题。
  • 快速而肮脏的解决方案是分配给sys.argv,然后不加改变地运行该代码:sys.argv = ['blah', '-u', username, '-p', password] + args
  • @Bakuriu 我以为我在这里展示了 main 代码?据我了解,就是从命令行运行脚本时调用的函数,对吧?
  • 按照惯例,在__main__ 块中通常调用一个名为main 的函数。您的措辞可能具有误导性,因此对其进行了编辑以避免混淆。如果在解析参数之后,脚本然后调用带有解析参数的工作函数,那么您可能可以直接调用该工作函数 - 这就是 Bakuriu 的意思。无论如何,脚本的作者将参数解析放在了错误的位置。
  • @wim 不幸的是,在这种情况下没有任何工作函数。它通向args = parser.parse_args(),然后在整个函数中使用args.uargs.p等。那么我是否有必要设计自己的函数来获取所有这些输入?我想尽可能多地保留代码。

标签: python function command-line


【解决方案1】:

怎么样:

def do_stuff(args):
    print(args.u)
    print(args.p)


def main(args_list=None):
    parser = argparse.ArgumentParser(description='get data', add_help=False, usage='this_script.py -u username -p password [options]')
    parser.add_argument('-u', metavar='<STR>', type=str, help='username')
    parser.add_argument('-p', metavar='<STR>', type=str, help='password')
    parser.add_argument('-i', metavar='<STR>', nargs='+', type=str, help='List of IDs')
    ...

    if args_list:
        args= parser.parse_args(args_list)
    else:
        args = parser.parse_args()

    do_stuff(args)


if __name__ == "__main__":
    main()

通过将以下行添加到您的包中 __init__.py

from .__main__ import main

您可以从命令行调用脚本作为函数,也可以从另一个脚本中调用,只要您导入包即可,例如

import <package name> as pkg
pkg.main(['-u', 'my_username', '-p', 'my_password'])

【讨论】:

    【解决方案2】:

    是的,简单的方法是使用stdlib runpy

    import runpy
    
    runpy.run_module(mod_name, init_globals=None, run_name=None, alter_sys=False)
    

    并且,请记住不要这样编写自己的脚本。条件下应该只有一行,并且应该是对不带参数的函数的调用,如下所示:

    if __name__ == "__main__":
        main()
    

    任何东西都太多了。

    【讨论】:

    • 所以我已经将所有代码从if __name__ == "__main__": 下移动到它自己的方法main(),但现在我不确定如何将任何输入传递给它,因为它没有参数.这样做的标准程序是什么?
    • 标准过程是让main解析来自CLI的参数,然后然后它调用一些其他函数do_stuff(...)并传入parsed i> 论据。在您的情况下,您可以直接调用do_stuff,因为您可能不需要从 CLI 解析用户名/密码,而是从文件或其他文件中读取它们。
    • 啊,我明白了。所以我现在想我需要重构所有以下代码,其中args.i,'args.u, and args.p`等被调用,通过为它们分配适当的变量名,这将是我的do_stuff()的参数函数,如def do_stuff(var_i=None, var_u=None, var_p=None, ...)。虽然,这里有 15-20 个变量;有没有更简单的方法来做到这一点?这需要我在 main() 内执行 var_i = args.i; var_p = args.p; ... 才能在该函数中调用它。这只是这里的入场费吗?
    • 是的。解析参数后,您实际上不应该传递 argparse 命名空间对象本身。它只是一个简单的容器,方便 parser.parse_args() 返回一个对象。
    • 这对我来说是有道理的,但是我很好奇,为什么parse_args() 方法返回一个Namespace 对象。与简单的dict 相比,这有什么优势?
    猜你喜欢
    • 1970-01-01
    • 2012-09-11
    • 1970-01-01
    • 1970-01-01
    • 2021-05-09
    • 2021-03-06
    • 2017-07-21
    • 1970-01-01
    • 2015-11-02
    相关资源
    最近更新 更多