4

I want to use argparse and have it so the number of arguments supplied determines which subroutine is executed.

For example, in the script below, I would like argparse to be able to execute the following:

dmsconvert.py 12.5
>>> (12, 30, 0)

dmsconvert.py 12 30 0.0
>>> 12.5

Instead, the only way I have been able to do this with argparse is to have an explicit option, i.e:

dmsconvert.py 12.5
>>> (12, 30, 0)

dmsconvert.py -a 12 30 0.0
>>> 12.5   

Can anyone suggest a way to achieve my preferred approach using argparse? Note: I want the auto-generated argparse help text to look sensible.

Full code example:

import argparse
import sys

def dms_to_decimal(deg,min,sec):
    assert float(min) < 60.0, 'Mintue value: %s must be less than 60' % float(min)
    assert float(sec) < 60.0, 'Second value: %s must be less than 60' % float(sec)
    return float(deg)+float(min)/60.0+float(sec)/(60.0*60.0)

def decimal_to_dms(deg):
    min = 60.0*(deg-int(deg))
    sec = 60.0*(min-int(min))
    return int(deg),int(min),sec

parser = argparse.ArgumentParser(description = 'Convert decimal degrees to dms and visa versa')
parser.add_argument('-a',dest='dms_args',nargs=3)
parser.add_argument(dest='dec_arg',type=float,nargs='?')
args = vars(parser.parse_args(sys.argv[1:]))

if args['dms_args'] is not None:
    print dms_to_decimal(*args['dms_args'])

if args['dec_arg'] is not None:
    print decimal_to_dms(args['dec_arg'])
4

1 回答 1

1

可以通过自定义操作来完成:

import argparse

class OneOrThree(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if len(values) == 1:
            setattr(namespace, self.dest, values)
            setattr(namespace, 'result', decimal_to_dms(*values))
        elif len(values) == 3:
            setattr(namespace, self.dest, values)
            setattr(namespace, 'result', dms_to_decimal(*values))
        else:
            raise parser.error('Expected 1 or 3 arguments')

def dms_to_decimal(deg,minute,sec):
    assert float(minute) < 60.0, 'Mintue value: %s must be less than 60' % float(minute)
    assert float(sec) < 60.0, 'Second value: %s must be less than 60' % float(sec)
    return float(deg)+float(minute)/60.0+float(sec)/(60.0*60.0)

def decimal_to_dms(deg):
    minute = 60.0*(deg-int(deg))
    sec = 60.0*(minute-int(minute))
    return int(deg),int(minute),sec

parser = argparse.ArgumentParser(
    usage='%(prog)s deg [minute, sec]',)
parser.add_argument('dms_args', nargs='+',
                    type=float,
                    action=OneOrThree,
                    metavar='deg [minute, sec]',
                    help='Given one arg, returns dms. Given three args, returns deg')
args = parser.parse_args()
print(args.result)

运行上述代码会产生如下行为:

% test.py 12.5
(12, 30, 0.0)

% test.py 12 30 0.0
12.5

如果提供的参数数量不正确,则会引发错误:

% test.py 12 30
usage: test.py [-h] dms_args [dms_args ...]
test.py: error: Expected 1 or 3 arguments

帮助消息如下所示:

% test.py -h

usage: test.py deg [minute, sec]

positional arguments:
  deg [minute, sec]  Given one arg, returns dms. Given three args, returns deg

optional arguments:
  -h, --help         show this help message and exit
于 2013-08-08T02:49:32.640 回答