【问题标题】:python argparse always set defaults related to an optional argumentpython argparse 总是设置与可选参数相关的默认值
【发布时间】:2015-05-12 02:30:52
【问题描述】:

我使用 python 3,我想将 argparse 设置为始终设置与我选择的预定义可选参数相关的默认键。

例如我将一些输入文件传递给脚本。可选参数指定输入文件是否应由脚本旋转。我得到一个输入文件列表,但我还想要一个相同大小的列表,其中包含用户想要旋转输入文件的“真”,或者当用户没有做出选择时默认为假

例如

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--input', action='append', required=True, help='Input pdf files.')
parser.add_argument('--rotate', action='store_true', default=False, help='Rotate the input file')
args = parser.parse_args()
print(args)

我用这个命令调用脚本:

python argparse_test.py --input 1.pdf --input 2.pdf --rotate

这是输出

命名空间(input=['1.pdf', '2.pdf'], rotate=True)

但我想要这个输出

命名空间(input=['1.pdf', '2.pdf'], rotate=[False,True])

请注意--rotate 可以是我在--input 文件上编写的python 脚本必须在--rotate 本身之前执行的任何操作。所以我需要知道--input 文件--rotate 指的是哪个

【问题讨论】:

  • 我不认为这是开箱即用的。您是否考虑过更改命令,以便 --rotate 参数将文件旋转(而 --input` 将文件不旋转?像这样:python argparse_test.py --input 1.pdf --rotate 2.pdf。然后你得到:Namespace(input=['1.pdf'], rotate=[ '2.pdf'])。跨度>
  • 这不是预期的行为。如果输入文件被“旋转”并不重要。旋转是由脚本完成的操作。一般来说,我想告诉 python 必须只对一个输入文件而不是其他输入文件执行某个操作。可以是轮换,也可以是其他。 '--rotate' 是必须仅在最后一个 '--input' 文件上执行的操作,并且只能在该文件上执行。
  • 你可能会更幸运地使用一个简单的配置文件来指定这些更复杂的操作,而不是试图让 argparse 做更多的事情。例如。类似docs.python.org/2/library/configparser.html

标签: python default argparse optional-parameters


【解决方案1】:

所以

python argparse_test.py --input 1.pdf --input 2.pdf --rotate

应该意味着 - 为 2.pdf 设置 rotate 为 True 因为它遵循该名称,但为 1.pdf 设置为 False 因为它后面没有 --rotate 标志?

它不能那样工作。像--rotate--input 这样的标记参数可以以任何顺序出现。并且store_true 参数不能像您的--input 那样在append 模式下运行。

一些可能有效的替代方案:

--input as nargs=2
python argparse_test.py --input 1.pdf 0 --input 2.pdf 1
Namespace(input=[['1.pdf','0'], ['2.pdf','1']])

--input as nargs='+'
python argparse_test.py --input 1.pdf --input 2.pdf 1
Namespace(input=[['1.pdf'], ['2.pdf','1']])

也就是说,用户在文件名后面加了一个字符串,表示是否要旋转。

我本来打算为rotate 推荐一个append_const,但无法在列表中插入默认值。

我可以想象定义一对自定义动作类 - 见下文。

我认为 Waylan 的建议是有 2 个 append 列表,一个用于应该旋转的文件,另一个用于不应该旋转的文件。

您将如何在使用和帮助信息中传达这样的要求?在开发过程中对您来说可能看起来合乎逻辑的东西,对于其他用户或六个月后的您自己可能并不那么明显。


自定义动作类似乎表现得如你所愿。这不是一个微不足道的定制,但也不是一个复杂或晦涩的定制。它确实需要对argparse 代码有一些详细的了解。

import argparse

class Input_action(argparse._AppendAction):
    def __call__(self, parser, namespace, values, option_string=None):
        # assume attribute has already been set to []
        items = getattr(namespace, self.dest)
        items.append(values)
        setattr(namespace, self.dest, items)

        dest = 'file_rotate'
        values = False
        items = getattr(namespace, dest)
        items.append(values)
        setattr(namespace, dest, items)

class Rotate_action(argparse._StoreTrueAction):

    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, self.const)
        dest = 'file_rotate'  # not same as self.dest
        items = getattr(namespace, dest)
        if items:
            items[-1] = True
        setattr(namespace, dest, items)

parser = argparse.ArgumentParser()
parser.add_argument('-i','--input', action=Input_action)
parser.add_argument('-r','--rotate', action=Rotate_action)

parser.print_help()

# simpler to initialize these attributes here than in the Actions
ns = argparse.Namespace(input=[], file_rotate=[])
print parser.parse_args(namespace=ns)

输出:

1030:~/mypy$ python stack28967342.py -i one -r -i two -i three -r
usage: stack28967342.py [-h] [-i INPUT] [-r]

optional arguments:
  -h, --help            show this help message and exit
  -i INPUT, --input INPUT
  -r, --rotate
Namespace(file_rotate=[True, False, True], input=['one', 'two', 'three'], rotate=True)

注意生成的命名空间中的 2 个列表。 rotate 属性不是必需的,但删除它需要更多的工作。 help 没有表示任何关于选项的特殊配对。


这是一个备用动作对,它与一个列表和具有名称和旋转属性的复合对象一起使用。推广这种方法可能更容易。

class MyObj(argparse._AttributeHolder):
    "simple class to hold name and various attributes"
    def __init__(self, filename):
        self.name = filename
        self.rotate = False
        # define other defaults here

class Input_action(argparse._AppendAction):
    def __call__(self, parser, namespace, values, option_string=None):
        items = argparse._ensure_value(namespace, self.dest, [])
        items.append(MyObj(values))
        setattr(namespace, self.dest, items)

class Rotate_action(argparse._StoreTrueAction):

    def __call__(self, parser, namespace, values, option_string=None):
        # with default=SUPPRESS, rotate does not appear in namespace
        dest = 'input'  # could make this a parameter
        items = getattr(namespace, dest)
        if items:
            items[-1].rotate = True
        # no need to set, since I'm just modifying an existing object

parser = argparse.ArgumentParser()
parser.add_argument('-i','--input', action=Input_action,
    help='create a default input object with this name')
parser.add_argument('-r','--rotate', action=Rotate_action, default=argparse.SUPPRESS,
    help = 'set rotate attribute of preceeding input object')

parser.print_help()
print parser.parse_args()

产生类似的参数

Namespace(input=[MyObj(name='one', rotate=True), MyObj(name='two', rotate=False), MyObj(name='three', rotate=True)])

【讨论】:

  • 我只使用一个附加列表添加了一个变体。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-22
  • 2021-10-16
  • 2011-02-13
  • 1970-01-01
  • 2014-12-07
  • 2023-01-11
  • 2018-03-26
相关资源
最近更新 更多