How do I use *args with class inheritance in python?

Viewed 45

I'm creating software for taking notes in history. It's essentially going to be a big timeline so that you can enter any person, place, or thing along with a date and it organizes everything into a txt file. The class "Note" is the parent of any and all information being put into the timeline. This class takes arguments: self, *args, id='default', start_date='iso_date', end_date='iso_date', loc='some location'. I am using "*args" so that you can enter an arbitrary amount of information about a given item. \nEx:

class Note(object):
    def __init__(self, *args, id="default", start_date=None, end_date=None, loc="default"):
        if start_date: start_date = date.fromisoformat(start_date)
        if end_date: end_date = date.fromisoformat(end_date)
        self.id = id.title()
        self.facts = [f for f in args]
        self.loc = loc

This is so simple I couldn't break it. The problem arises when I created a child class for books specifically.

class Book(Note):
    def __init__(self, *args, id='default', start_date=None, end_date=None, loc='default', author='Jane Doe'):
        super().__init__(self, *args, id=id, start_date=start_date, end_date=end_date, loc=loc)
        self.author = author

I have to use the super function here so that it initializes as usual, but I can still create a new variable "author". And here is the problem.

PS C:\Users\Max\projects\hist\Hist> py                                        Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from objects import Book
>>>
>>> var = Book("It's about a whale.", "There is a captain.", id="Moby Dick", start_date="1851-10-18", loc="Pittsfield, Massachusetts")
>>>
>>> var.id
'Moby Dick'
>>>
>>> var.loc
'Pittsfield, Massachusetts'
>>>
>>> var.author
'Herman Melville'
>>>
>>> var.facts
[<objects.Book object at 0x0000010E08708160>, "It's about a whale.", 'There is a captain.']
>>>

Somehow, the super function is taking the child class as an argument which makes sense but I haven't dug through enough of the documentation to find a way to circumvent the issue. I've tried using

super().__init__(self, args[1:], id=id, start_date=start_date, end_date=end_date, loc=loc)

producing this:

>>> var.facts
[<objects.Book object at 0x0000017ADA3D8160>, ('There is a captain',)]

I have no idea how to drop that argument. Someone help me I'm very dumb.

1 Answers

When calling the parent class constructor via super(), note that the __init__ method is already bound. So like calling any other bound method, the self part is passed implicitly and doesn't need to be an explicit argument.

To wit:

class Parent: 
    pass                                                                         

class Child(Parent): 
    def __init__(self): 
        print(super().__init__.__self__) 

c = Child()  
# prints <__main__.Child object at 0x105116e48>

Typically, your code may have failed with some sort of TypeError on the number of arguments, but since you have *args and no type validation that all the args must be strings, it happily take the extra self as a fact when passed to the superclass

TL;DR just drop the explicit self arg when calling super()

super().__init__(*args, id=id, start_date=start_date, end_date=end_date, loc=loc)
Related