首页 文章

用docopt替换sys.argv

提问于
浏览
-2

我正在努力合并一些字符串替换,目前使用 sys.argv[i] 将参数传递给我的脚本 . 到目前为止,我发现文档相对不清楚 .

我的代码目前的工作方式是

filename.py -param_to_replace new_param_value

(我还可以包含多个参数来替换)

然后由此处理

if len(sys.argv) > 1:
    for i in range((len(sys.argv)-1) / 2):
        params[sys.argv[1+2*i].split('-')[1]] = float(sys.argv[1+2*i+1])

其中params是一组已定义参数的名称 .

我想我应该能够使用docopt,但到目前为止我所拥有的更像是

"""Docopt test
Usage:
  filename.py --param_name1 <val> --param_name2 <val>

  filename -h | --help
  filename --version

Options:
  -h --help     Show this screen.
  --param_name1 Change some param we call param_name1, all other params changed in similar way


"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='filename 1.0')
    print(arguments)

但这并未传递任何内容,似乎是官方文档中提供的详细信息的结尾 . 有更熟悉docopt的人是否知道如何更有效地传递命令行参数?或者我应该在此之后用“arguments”替换sys.argv?

谢谢!

4 回答

  • 0

    而不是使用 docopt (这无论如何都很棒),如果你只是想 Build 一个 -param 的字典: value ,因为docopt会返回你可以用字典理解来做到这一点(我想以比你现在正在做的更容易理解的方式) ):

    if __name__ == '__main__':
        if len(sys.argv) > 1:
            args = sys.argv[1:]
            assert len(args) % 2 == 0
            arguments = {args[i][1:]: args[i+1] for i in range(0, len(args), 2)}
            print(arguments)
    

    然后测试一下:

    In [12]: runfile(
                 'test.py', wdir='/home/mgc',
                  args='-param_to_replace1 new_value1 -param_to_replace2 new_param_valu2')
    
    {'param_to_replace2': 'new_param_valu2', 'param_to_replace1': 'new_value1'}
    
  • 0

    我刚刚意识到 __doc__ 正在做什么(见我的评论) .

    加载模块时,会将初始三重引用字符串分配给其 __doc__ 变量 . 通常这是各种'help'包可以使用的文档 . 函数和类也有 __doc__ . 对于 docopt 使用,它应该模拟命令行解析器的 help 显示 .

    arguments = docopt(__doc__, version='filename 1.0')
    

    line将此 __doc__ 字符串传递给 docopt 函数(或类) . 这推断出你想接受的参数,解析 sys.argv ,并返回一个字典 arguments .

    在您的示例中,使用行是:

    filename.py --param_name1 <val> --param_name2 <val>
    

    如果你用脚本调用脚本,我希望如此

    filename.py --param_name1 foo --param_name2 bar
    

    arguments 将是一个字典,看起来像:

    {'param_name1':'foo', 'param_name2':'bar'}
    

    arguments['param_name1'] 应该返回'foo'

    您当前的代码显然试图解析输入,如

    filename.py -foo 2.323 -xxx 1.23
    

    并执行

    params['foo'] = 2.323
    params['xxx'] = 1.23
    

    我希望 docopt 版本会在输入时产生错误,但我不知道它处理错误的细节 .

    Better solution than if name == 'main' twice in Python script是使用 python docopt 的较长示例 .

    argparse版本

    这是一个 argparse 脚本,我认为它的行为就像你的 sys.argv 解析一样 . 它在 params 字典中定义了一组基于 argparse 参数的条目,并且应该返回兼容的字典 .

    import argparse
    
    params = {'foo':None, 'bar':None, 'test':0}
    
    parser = argparse.ArgumentParser()
    for key in params:
        name = '--'+key
        default = params[key]
        if default is None:
            parser.add_argument(name)
        else:
            # could be more elaborate on setting type
            parser.add_argument(name, default=default, type=float)
    
    parser.print_help()
    print(params)
    print(vars(parser.parse_args([]))) # no arguments, should == params
    args = parser.parse_args()  # test the sys.argv
    print(args)   # namespace
    print(vars(args))  # dictionary equivalent
    

    生产环境 :

    1033:~/mypy$ python stack34956253.py --foo 124 --test 323.3
    usage: stack34956253.py [-h] [--test TEST] [--foo FOO] [--bar BAR]
    
    optional arguments:
      -h, --help   show this help message and exit
      --test TEST
      --foo FOO
      --bar BAR
    {'test': 0, 'foo': None, 'bar': None}
    {'test': 0, 'foo': None, 'bar': None}
    Namespace(bar=None, foo='124', test=323.3)
    {'test': 323.3, 'foo': '124', 'bar': None}
    

    这可能会以相同的方式工作:

    argparse_help = parser.format_help()
    arguments = docopt(argparse_help, version='filename 1.0')
    
  • 0

    看起来你的docopt语法有问题 . 对我来说,使用web docopt tool计算docopt语法很有帮助 . 该设置可以很容易地快速迭代语法,看看它是否会产生一个对我的程序有用的结构 .

    我把你的语法放在here中,带有参数 --param_name1 foo --param_name2 bar 并得到了这个结果:

    {
      "--param_name1": "foo", 
      "--param_name2": true, 
      "<val>": "bar"
    }
    

    在第一部分中,您指定了: Usage: filename.py --param_name1 <val> --param_name2 <val> . 请注意,您在2个不同的位置使用 <val> ,这意味着只填充第2个值 . 在第二部分中,您再次指定: Options: --param_name1 ,这是多余的,因为您已在使用部分中指定了相同的 --param_name1 . 论证应该在一个但不是两个部分 .

    Here是一个简化的语法:

    Usage:
      filename --old=<old> --new=<new>
    

    使用 --old=foo --new=bar 解析的结果是:

    {
      "--new": "bar", 
      "--old": "foo"
    }
    

    或者完全删除开关名称,只使用位置参数 . This可以更容易地预测要替换的多个参数 .

    Usage:
      filename <new> <old>...
    

    使用 bar foo baz 参数,这是解析的结构:

    {
      "<new>": "bar", 
      "<old>": [
        "foo", 
        "baz"
      ]
    }
    

    我只是blogged关于我有多喜欢docopt,所以我希望你能克服最初的理解障碍 .

  • 0

    所以我想我会回到这个,因为我意识到我从未关闭过主题并发布了答案 . 传递参数的正确方法如下:

    """Usage: filename.py [--arg1=N] [--arg2=N] 
    
    Options:
        --arg1=N passes arg1
        --arg2=N passes arg2
    """
    
     from docopt import docopt 
     arguments = docopt(__doc__,version='')
     print arguments[--arg1] #This is to print any argument or pass it through
    

    似乎只有一些使用问题,这在代码中清除了 .

相关问题