0

First of all, this question might be duplicate, but somehow I haven't got my head around those answers. Say I have:

import argparse
class Parent:
    def __init__(self):
        self.parser = argparse.ArgumentParser()

        self.parser.add_argument("user")
        # parse the argument
        self.parser.parse_args()

class Child(Parent):
    def __init__(self):
        # here self.parser is not defined
        # self.parser.add_argument("password")
        super().__init__()
        # here args are already parsed and 
        # self.parser does not include args
        # self.parser.add_argument("password")
if __name__ == '__main__':

    Child()

This is the minimal working example of the code I want to write (something like command line app framework. My setup is basically the following

class Parent:
    def __init__(self):
        # do some stuff starting stuff
       # middle
        # do some ending stuff

class Child(Parent):
    def __init__(self):
        # do something in the middle of Parent init

There is simple partial workaround in my case. When Parent might not do self.parser.parse_args() or it would somehow (don't know how write now exactly) check, if child added any arguments and then leave the self.parser.parse_args() to the child class. But I thought, whether it is possible one dumb idea was to change parent's __init__ to behave like a decorator over child and do something like this (a part from the fact, that init does allow to return anything other than None, it probably does not work from various other reasons..?)

class Child(Parent):
    @super.__init__
    def __init__(self):

So, any ideas, which path should I chose? My motivation for that is, that I want the end user, who would use such framework (that means writing app, that inherits from Parent) to need to do as little work as possible (that means, moving most things into Parent). Is there any reasonable way, how to do this?

Community
  • 1
  • 1
quapka
  • 2,799
  • 4
  • 21
  • 35
  • Why are you using a class for this, if all the functionality is in `__init__`? Have you considered splitting out the setup and parsing, so your last line would be e.g. `Child().parse()`? – jonrsharpe May 21 '16 at 07:41
  • @jonrsharpe This is just a start I thought of expanding it more. – quapka May 21 '16 at 08:15

1 Answers1

2

Move the 'middle' part in the parent class out to a separate method. You can then override just that method in the child:

class Parent:
    def __init__(self):
        self._construct_parser()
        # parse the argument
        self.parser.parse_args()

    def _construct_parser(self):
        self.parser = argparse.ArgumentParser()    
        self.parser.add_argument("user")

class Child(Parent):
    def _construct_parser(self):
        super()._construct_parser()
        # add on to self.parser

This is essentially what at least one of the answers on the other question tells you to do; split out the various parts in the parent __init__ into separate methods that can each be overridden separately.

If you are making this a framework, then adding in specific hooks is also an option. The parent defines empty methods that it calls at specific points, that a child class can implement to participate in specific steps. Here that'd be where the ArgumentParser class has been constructed and a child class could add to it:

class Parent:
    def __init__(self):
        self._construct_parser()
        # parse the argument
        self.parser.parse_args()

    def _construct_parser(self):
        self.parser = argparse.ArgumentParser()    
        self.parser.add_argument("user")
        self.update_parser(self.parser)

    # hook method
    def update_parser(self, parser):
        pass

class Child(Parent):
    def update_parser(self, parser):
        # add to parser

Now the child class doesn't even have to use super() anymore; it is handed all it needs, pre-digested by the framework, to update the parser.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks, great. I don't know, why I was so eager to have it inside `__init__`, maybe because I thought of that as a setup for the app (which it is).. Just a side question: _would you use this code yourself_ ? Is it good practice? – quapka May 21 '16 at 08:53
  • @quapka: I'd not use it, no, because usually for my use-cases there already is something available. Either I'd use argparse in a `main()` *function*, for more complex commandline needs use [`click`](http://click.pocoo.org/5/), or use a context-specific tool like [Flask-Script](https://flask-script.readthedocs.io/en/latest/) or [add a Django command](https://docs.djangoproject.com/en/1.9/howto/custom-management-commands/). – Martijn Pieters May 21 '16 at 08:57