【问题标题】:How to prevent multiple occurrence of an optional argument with choices如何防止多次出现带有选项的可选参数
【发布时间】:2020-04-30 20:51:12
【问题描述】:

我在 python 2.7 中使用 argparse。我想阻止用户多次调用 my_app.py --cache 可选参数。 -cache(或--cache)是带有选项的可选参数,有一个常量和一个默认值。代码:

parser = argparse.ArgumentParser()
parser.add_argument("-cache","-- 
cache",required=False,const='all',default=None,nargs='?',choices=["server-only","local-only","all"], 
help="Activate Cache by choosing one of the list choices. e.g. -cache=local-only")

我想在用户以下面的形式调用 my_app.py 时引发异常:

#if he calls with multiple --cache arguments, argparse takes the last dilvered one !! But i want to 
raise an exception here!
my_app.py --cache --cache=server-only

链接multiple argument occurrence 中的类似问题没有足够的答案

【问题讨论】:

  • 一种解决方案是将选项值保存在列表中。然后,在解析选项后,检查列表长度并给出错误是 2 或更多。使用action="append"default=[] 收集列表中的选项值。
  • @TomKarzes - 很棒且快速的答案。在我发布我的问题之前,我已经尝试过完全了解这个概念(同样的想法),并且我已经将默认值更改为保存一个列表,但我唯一错过的是 action="apend"!谢谢。
  • 默认的argparse 行为是将default 放在命名空间中,然后允许每个实例覆盖它,实际上以最后一个用户提供的值(如果有)结束。为什么要这么拼?您的用户是否特别喜欢出于某种特殊目的使用--cache --cache=foobar

标签: python argparse


【解决方案1】:

您可以定义一个自定义操作,在第一次使用该选项时“记住”该选项,然后在第二次使用时引发异常。

import argparse


class OneTimeAction(argparse._StoreAction):
    def __init__(self, *args, **kwargs):
        super(OneTimeAction, self).__init__(*args, **kwargs)
        self.seen = False

    def __call__(self, *args, **kwargs):
        if self.seen:
            parser = args[0]
            option_string = args[3]
            parser.error("Cannot use {} a second time".format(option_string))
        super(OneTimeAction, self).__call__(*args, **kwargs)
        self.seen = True


parser = argparse.ArgumentParser()
parser.add_argument("-cache", "--cache", 
                    action=OneTimeAction,
                    default="all",
                    choices=["server-only", "local-only", "all"],
                    help="Activate Cache by choosing one of the list choices. e.g. -cache=local-only")

更一般地说,您可以将其定义为与任何类型的操作一起使用的混合。以下示例还将参数的大部分配置折叠到自定义操作本身中。

import argparse


class OneTimeMixin(object):
    def __init__(self, *args, **kwargs):
        super(OneTimeMixin, self).__init__(*args, **kwargs)
        self.seen = False

    def __call__(self, *args, **kwargs):
        if self.seen:
            parser = args[0]
            option_string = args[3]
            parser.error("Cannot use {} a second time".format(option_string))
        super(OneTimeMixin, self).__call__(*args, **kwargs)
        self.seen = True


class CacheAction(OneTimeMixin, argparse._StoreAction):
    def __init__(self, *args, **kwargs):
        # setdefault ensures you can override these if desired
        kwargs.setdefault('choices', ["server-only", "local-only", "all"])
        kwargs.setdefault('default', 'all')
        kwargs.setdefault('help', "Activate Cache by choosing one of the list choices. e.g. -cache=local-only")
        super(CacheAction, self).__init__(*args, **kwargs)


parser = argparse.ArgumentParser()
parser.add_argument("-cache", "--cache", action=CacheAction)

【讨论】:

  • 这是 OOP 的最佳状态。让我想起了设计模式(4 人)的 SOLID 原则之一——对扩展开放,对修改关闭。您的解决方案以良好的方式扩展了课程。谢谢。
  • 在类OneTimeAction中,在基类的call方法中,基类的真正签名是:__call__(self, parser, namespace, values, option_string=None) ,所以 python 会阻止你这样做。您需要从对超级方法的调用中删除 **kwargs
  • @Adam 如果不传递任何额外的关键字,则无效。接受未知的关键字参数并传递它们是正确使用 super 的标志之一。
  • 你写的第一个函数 call 是错误的 def __call__(self, parser, namespace, values, option_string=None,**kwargs) !反正你自己修好了!谢谢你。我仍然对 *args 和 **kwargs 感到困惑,如果你有一个很好的教程链接,如果你能提供它,我将不胜感激。 (对于高级案例(例如您在此处使用的案例,而不是像 realpython.com/python-kwargs-and-args 这样的标准教程,例如,为什么 python 在同一函数中使用其内置函数,例如 def foo(*args, **kwargs) ??为什么不单独使用**kwargs!?
  • 如果单独使用**kwargs,那么调用者必须将所有参数作为关键字参数传递。
猜你喜欢
  • 2021-02-28
  • 2012-07-16
  • 2017-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多