2

I want to parse an option with multiple arguments and return a value as a collection of my choice, e.g. a tuple or an np.array rather than a list.

I could simply convert the resulting list after parsing as suggested in this answer,

parser.add_argument('--foo', nargs='*')
options = parser.parse_args('--foo 1 2 3'.split())
# manual casts go here
options.foo = tuple(options.foo)

but this could be cumbersome and error prone esp. if we have many such parameters to convert.

Is it possible to have ArgParser itself store arguments in a collection other than a list?

Community
  • 1
  • 1
P-Gn
  • 23,115
  • 9
  • 87
  • 104
  • `argparse` is parsing a list, e.g. `['--foo','1','2','3']`. WIth `nargs='*'` is slices off a sublist, passes the elements through the `type` function, and does a simple `setattr(namespace, dest, values)`. With the `append` action it appends those lists to an existing list (or []). – hpaulj May 16 '18 at 16:42
  • You can customize the action as suggested, but is that any less error prone? The primary purpose of `argparse` is to figure out what your user wants. Massaging the results after parsing is quite acceptable, and often easier. When would have a list instead of a tuple cause problems? Who in their right mind wants to enter a whole `numpy` array via the commandline? :) – hpaulj May 16 '18 at 16:45
  • @hpaulj You don't see the use, but I do. For example most of my numeric inputs ends up in `np.array`s. (yes, "whole" vectors of 2 or 3 elements :-). I find having to do these manual conversions after arg parsing a burden -- and ugly, if that even matters. – P-Gn May 16 '18 at 18:37

1 Answers1

4

It is possible by creating a custom action. For example,

import argparse
import numpy as np

def collect_as(coll_type):
  class Collect_as(argparse.Action):
    def __call__(self, parser, namespace, values, options_string=None):
      setattr(namespace, self.dest, coll_type(values))
  return Collect_as

parser = argparse.ArgumentParser()
parser.add_argument('--foo', nargs='*', type=int, action=collect_as(tuple))
parser.add_argument('--bar', nargs='*', type=np.float32, action=collect_as(np.array))
options = parser.parse_args('--foo 1 2 3 --bar 4 5 6'.split())
print(options)
# Namespace(bar=array([4., 5., 6.], dtype=float32), foo=(1, 2, 3))
P-Gn
  • 23,115
  • 9
  • 87
  • 104