2

I have a class which has around a dozen object variables. Along with each variable, I want to provide a default value, a help string (for argparse) and a comment string (to write to a data file). I want to be able to instantiate objects by:

  1. providing explicit values to the init method
  2. providing values to use on the command line
  3. taking the defaults
  4. some combination of the above.

When I only had two object variables, I provided the defaults in the declaration of the init function, I replicated these and the help string when I created the argument parser, etc. But for many variables, it gets very messy.

The trimmed down example code below is my current solution, which I am fairly happy with, does exactly what I want, and works pretty well. But, I have two questions:

  1. Is it pythonic?
  2. Surely this must be a solved problem already, and there is a "standard" way to do it?

I did look around here, and Googled a bit, but I didn't manage to find an existing solution.

# invoke with python demoArgs.py -a 15 -b 25 -c text

import argparse

class Foo:
    defaults = {'a':10,'b':20, 'c':"outFile"}
    helpDefs = {'a' : 'the first parameter',
                'b' : 'the second paramter',
                'c' : 'the third paramter'}

    @staticmethod
    def parse_args():
        parser = argparse.ArgumentParser()
        for key in Foo.defaults:
            parser.add_argument('-'+ key, help = Foo.helpDefs[key], 
                                  default = Foo.defaults[key])
        return vars(parser.parse_args())

    def __init__(self, a = defaults['a'], b = defaults['b'], c = defaults['c']):
        self.a = a
        self.b = b
        self.c = c

    def report(self):       
        for key in sorted(vars(self)):
            print key, "val = ",  getattr(self,key), \
                       ", help = ", self.helpDefs[key], \
                       ", def = ",  self.defaults[key]

def main():
    print "\n an object using all of the defaults"
    a = Foo()
    a.report()

    print "\n an object using the command line values"
    args = Foo.parse_args()
    b = Foo(**args)
    b.report()

    print "\n an object using values specified in the code"
    c = Foo(30,40,"object")
    c.report()

    print "\n an object using a perverse combination"
    args = Foo.parse_args()
    d = Foo(50, c = args['c'])
    d.report()


if __name__ == '__main__':
   main()
user3443148
  • 411
  • 1
  • 4
  • 5
  • You could consider using the `inspect` module to pull the default values out of the signature for `__init__` rather than using your `default` dictionary – Eric Dec 11 '15 at 18:07
  • I don't see any problems (other than the display indentation). `ipython` populates its `argparse` with arguments derived from `config` files. Thus parameters can be set with default configs, custom configs, or as a last resport the commandline. – hpaulj Dec 11 '15 at 18:18
  • `plac` is a `pypi` package that builds on top of `argparse`. It populates a `parser` by doing `inspection` on one or more functions, defining an argument for each parameter of the function. You use a decorator to add information (or a rarely use Py3 annotation syntax). It may also work with a `class` `__init__`. It may give you further ideas. – hpaulj Dec 11 '15 at 18:24
  • ``\`` inside parentheses (your `__init__` argument list) is unpythonic – Eric Dec 11 '15 at 18:27
  • You should have a look at [Click](http://click.pocoo.org/5/). It is very feature and might provide what you want. – Mike Müller Dec 11 '15 at 18:42

1 Answers1

0

As for is it "pythonic" -- I'd say no. Argparse is already pretty powerful. Someone who wanted to use your library would have to carefully understand how it worked and how it wrapped something else (which would also require understanding the thing it wrapped). It's arguable whether the effort would be worth it. In the short run your probably better off just using argparse, rather than trying to simplify something which already does the job (IMHO).

As for question #2 -- I seriously doubt anyone else has tried to do it. Most people will usually just stick with the stdlib. It's simpler and is available on all platforms where python can run.

Of course neither of these answers should stop you from doing what you like.

user590028
  • 11,364
  • 3
  • 40
  • 57
  • I agree argpase is very powerful. But, if I just want to initialize an object from e.g. another function, I might not have command line arguments. Regarding whether someone else can understand this, it is only intended for my use. :) But I didn't want to define e.g. the defaults in one location, and the comment strings somewhere else, – user3443148 Dec 11 '15 at 18:23