d.sci
d.sci

Reputation: 15

How do I wrap argparse options into the python class?

I am pretty new to python OOP, so I got some confusion.

Currently I have:

parser = argparse.ArgumentParser(description='script 1.0')
parser.add_argument('-a', '--foo', help='specify foo')
parser.add_argument('-b', '--bar', type=int, help='specify bar')
parser.add_argument('-c', '--baz', help='specify baz')
parser.add_argument('-d', '--bar2', help='bar2')

args = parser.parse_args()

foo = args.foo
bar = args.bar
baz = args.baz
bar2 = args.bar2

which works pretty well, but I wan to create a class for the whole of my script and make argparse as a class's method (is it possible at all?).

So I tried:

     import argparse
         ....

        Class Program:
             def __init__(self, foo, bar, baz, bar2):
                 self.foo = foo
                 self.bar = bar
                ...(so on for each arg)
             def main():
                 parser = argparse.ArgumentParser(description='script 1.0')
                 parser.add_argument('-a', '--foo', help='specify foo')
                 parser.add_argument('-b', '--bar', type=int, help='specify bar')
                 parser.add_argument('-c', '--baz', help='specify baz')
                 parser.add_argument('-d', '--bar2', help='bar2')

                 args = parser.parse_args()

                 foo = self.foo
                 bar = self.bar
                 baz = self.baz
                 bar2 = self.bar2

I do not think I am doing right, though. I have not found too much info about it but one post on SO which did not clarify situation to me, so I want to have opinions for my specific case

Upvotes: 1

Views: 4367

Answers (2)

Rea Haas
Rea Haas

Reputation: 2528

I would do it this way:

import argparse


Class Program:
    def __init__(self, foo, bar, baz, bar2):
        self.foo = foo
        self.bar = bar
        ...(so on for each arg)
    
    def do_things():
        pass

         
def get_args():
    parser = argparse.ArgumentParser(description='script 1.0')
    parser.add_argument('-a', '--foo', help='specify foo')
    parser.add_argument('-b', '--bar', type=int, help='specify bar')
    parser.add_argument('-c', '--baz', help='specify baz')
    parser.add_argument('-d', '--bar2', help='bar2')

    return parser.parse_args()


def main(args):
    instance = Program(args.foo,
                       args.bar, 
                       args.baz, 
                       args.bar2)

    instance.do_things()


if __name__ == '__main__':
    args = get_args()
    main(args)

Explanation: Try to separate things... Parsing the arguments is done in the get_args function, and then pass the "args" to the main. The main function is in charge to initialize the object by the parameters that been passed as args and perform some work on the object.

Upvotes: 2

hpaulj
hpaulj

Reputation: 231665

argparse is already makes use of classes. argparse.ArgumentParser(...) creates a parser object. parser.add_argument(...) creates an Action object, and puts it in a parser list. It also returns it to your code, which may in some advanced uses be handy. parse_args() returns a argparse.Namespace object.

You can subclass the argparse classes to customize their performance.

But usually you don't need to create your own parser class - unless you need a lot of special behavior.

Here's what I'd consider to be a clean use of argparse:

import argparse

# your code
def main(args):
    foo = args.foo
    # other uses of args

def other(foo, bar):
    # code using values

def make_parser():
    parser = argparse.ArgumentParser(description='script 1.0')
    parser.add_argument('-a', '--foo', help='specify foo')
    parser.add_argument('-b', '--bar', type=int, help='specify bar')
    parser.add_argument('-c', '--baz', help='specify baz')
    parser.add_argument('-d', '--bar2', help='bar2')
    return parser

if __name__ == '__main__':
    parser = make_parser()
    args = parser.parse_args()

Wrapping the parser definition in a function is a good idea. In more complex cases it could be a class. But normally you only need one parser per script, so a function is enough. That function can be in the main body of the script, but actual use should be in a if __name__ block, so the script can be imported without using the parser.

This puts the args namespace in the global environment, where is accessible to all your code. It can be passed as to your functions, or selected values can be passed:

main(args)
other(args.foo, args.bar)
foo = args.foo
do_things3(foo)
d = vars(args) # args in dictionary form

It's a good idea to write your code so it works as imported classes and/or functions, and when run as a script. The argparse part sets values when run as a script. When imported, the importing script sets the necessary control values, possibly with its own parser.

Upvotes: 3

Related Questions