【问题标题】:Python argparse : mutually exclusive arguments with optional and positional argumentPython argparse:具有可选和位置参数的互斥参数
【发布时间】:2015-12-18 21:18:42
【问题描述】:

我想通过 argparse 库获得这个:

PROG --yesterday | begin-date [end-date]

我尝试将互斥组和参数组结合起来,但没有成功。

这个程序应该只接受:

PROG --yesterday
PROG 2015-11-12
PROG 2015-11-12 2015-11-15

是否可以使用 argparse 做到这一点?


谢谢hpaulj。查看最终结果:

import argparse
from datetime import datetime
import pytz


def argument_date(str_date):
    try:
        return datetime.strptime(str_date, "%Y-%m-%d").replace(tzinfo=pytz.utc)
    except ValueError as e:
        raise argparse.ArgumentTypeError(e)

parser = argparse.ArgumentParser(prog='PROG')
parser.usage = """PROG [-h] [--yesterday | start [end]]"""
parser.add_argument('start', type=argument_date, nargs='?', help='Start date (format YYYY-MM-DD)')
parser.add_argument('end', type=argument_date, nargs='?', help='End date (format YYYY-MM-DD)')
parser.add_argument('--yesterday', action='store_true', help='Only yesterday')

args = parser.parse_args()

if args.yesterday and args.start:
    raise parser.error("--yesterday option is not incompatible with start argument")

if not args.yesterday and not args.start:
    raise parser.error("--yesterday option or start argument should be filled")

if args.end and (args.start >= args.end):
    raise parser.error("end argument should be granter than start")

【问题讨论】:

  • 是否有任何真正的理由使用--yesterday 而不是允许yesterday 作为begin-date 的特殊值?
  • startend 参数应该是一个日期时间对象而不是一个简单的字符串。所以,我认为从数据的角度来看是不一致的。
  • 所有参数都是简单的字符串,直到你真正把它们变成datetimeobjects。在此之前检查“昨天”之类的特殊情况是一件相对简单的事情。

标签: python argparse


【解决方案1】:

--yesterday 是多余的,因为它只是将start_date 设置为昨天的快捷方式。相反,让“昨天”成为start_date 的允许值。事实上,您可以根据需要概括 datetime 以允许对任一参数使用其他缩写。例如:

def argument_date(str_date):
    # Not the most efficient to roundtrip like this, but
    # fits well with your existing code
    now = datetime.datetime.utcnow().date()
    if str_date == "yesterday":
        str_date = str(now - datetime.timedelta(1))
    elif str_date == "today"
        str_date = str(now)

    try:
        return datetime.strptime(str_date, "%Y-%m-%d").replace(tzinfo=pytz.utc)
    except ValueError as e:
        raise argparse.ArgumentTypeError(e)

完成此操作后,您的代码就变成了:

parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('start', type=argument_date, help='Start date (YYYY-MM-DD, yesterday, today)')
parser.add_argument('end', type=argument_date, nargs='?', help='End date (YYYY-MM-DD, yesterday, today)')

【讨论】:

  • 有点离题,但由于有效使用自定义类型type=argument_date.,这是 IMO 的方式。
【解决方案2】:

您最好的选择是在解析后测试值,如果需要,提供您自己的自定义usage

互斥组可以使用一个可选的位置,例如

group = parser.add_mutually_exclusive_group()
group.add_argument('-y','--yesterday', action='store_true')
group.add_argument('dates',nargs='?')

我认为它可以与 nargs='*' 一起使用,但我收到 ValueError: mutually exclusive arguments must be optional 错误。

所以一个可选的位置值可以工作,但是没有办法将此测试与 2 个可选的位置值一起使用。

parser.add_argument('--yesterday',action='store_true')
parser.add_argument('start',nargs='?')
parser.add_argument('end',nargs='?')

然后测试args.yesterdayargs.start is Noneargs.end is None。如果其中某些组合是错误的,请提出parser.error('....')

只要您能区分默认值和用户给定的值,解析后的测试就与您可能强制解析器执行的任何操作一样好。

考虑哪些使用信息对您的用户有意义也是一个好主意。例如

例如:

PROG  [--yesterday | [start [end]]]

不是 argparse 可以自动生成的。

【讨论】:

  • 在这种情况下,如果您不提供默认值,nargs='*' 只会引发 ValueError。如果您使用 default=[] 它应该可以正常使用 nargs ='*'
猜你喜欢
  • 1970-01-01
  • 2017-01-17
  • 2020-07-29
  • 2019-02-16
  • 2017-01-03
  • 2016-05-04
  • 2012-12-04
  • 2013-12-28
  • 2013-01-28
相关资源
最近更新 更多