-2

I'm working on incorporating some string replacements and currently arguments are passed to my script using sys.argv[i]. I'd like to replace sys with docopt, but I've found the documentation relatively unclear so far.

The way my code currently works is

filename.py -param_to_replace new_param_value

(I can also include multiple params to replace)

This then gets processed by

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])

where params is the name of a set of defined parameters.

I think I should be able to get this to work with docopt, but so far what I have is more like

"""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)

But this doesn't pass anything and seems to be the end of the details provided in the official documentation. Does anyone with more familiarity with docopt know how to more effectively pass the command line arguments? Or should I replace sys.argv with "arguments" after this?

Thanks!

4

4 回答 4

0

看来您的 docopt 语法有问题。对我来说,使用web docopt 工具来制定 docopt 语法很有帮助。该设置使快速迭代语法变得容易,并查看它是否会产生对我的程序有用的结构。

我把你的语法和参数放在这里--param_name1 foo --param_name2 bar,得到了这个结果:

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

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

是一个简化的语法:

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

解析结果--old=foo --new=bar为:

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

或者完全删除开关名称,只使用位置参数。使得预测要替换的内容的多个参数变得更容易。

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

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

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

我刚刚写了一篇关于我有多喜欢 docopt 的博客,所以我希望你能克服最初的理解障碍。

于 2016-01-27T15:41:57.770 回答
0

而不是使用docopt(无论如何都很棒),如果你只是想构建一个-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'}
于 2016-01-22T22:25:47.737 回答
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

似乎只是使用有一些问题,这在此处的代码中已清除。

于 2017-01-27T22:22:57.757 回答
0

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

当模块被加载时,初始的三引号字符串被分配给它的__doc__变量。通常这是各种“帮助”包可以使用的文档。函数和类也有__doc__. 为了docopt使用它,它应该模拟help命令行解析器的显示。

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

行将此__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版本会在此输入中产生错误,但我不知道它处理错误的详细信息。

比 if __name__ == '__main__' 在 Python 脚本中两次更好的解决方案是使用python docopt.

argparse 版本

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

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')
于 2016-01-23T17:43:07.047 回答