Rosey
Rosey

Reputation: 821

Python how to add __init__ param to subclass

I have a subclass sharing the __ init __ of it's base class:

class SubClass(BaseClass)
    def __init__(self, param, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.thing = param

The problem I have been having is the subclass __ init __ parameter "param" is being passed into the super().__init__(*args, **kwargs) as an extra parameter. This usually gives me an error like:

TypeError: __init__() takes from 1 to 2 positional arguments but 3 were given

I don't want that. I only want "param" to be used for these subclass instances. How do stop sending the extra param to the baseclass __ init __ while still being able to use it in the subclass __ init __? Example code to reproduce the issue:

from unittest import TestCase


class TestCaseSubClass(TestCase):

    def __init__(self, param, *args, **kwargs):
        super().__init__(*args, **kwargs)  # Just use whatever is in TestCase's init + our stuff
        self.thing = param
        print(self.thing)


class TestClass(TestCaseSubClass(param='bdfbdfb')):

        def test_stuff(self):
            print('test stuff here')

Or with just raw python, no import, why cant I do this? (same error)

class A(object):
    def __init__(self, athing='thing'):
        self.thing = athing
        print(self.thing)

class AB(A):
    def __init__(self, param, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.param= param
        print(self.param)


class ABC(AB(param='thh')):
    pass

ABCinstance = ABC()

Upvotes: 0

Views: 171

Answers (1)

Kevin
Kevin

Reputation: 76194

I'm interpreting this question as "how can I provide a default parameter to a subclass without defining an __init__ for it?". One possible way is to define the default value as a class attribute, which you access in the parent class' __init__:

from unittest import TestCase

class TestCaseSubClass(TestCase):
    _default_param = None
    def __init__(self, *args, **kwargs):
        param = kwargs.pop("param", self._default_param)
        super().__init__(*args, **kwargs)  # Just use whatever is in TestCase's init + our stuff
        self.thing = param

class TestClass(TestCaseSubClass):
    _default_param = "bdfbdfb"
    def test_stuff(self):
        print('test stuff here')

x = TestClass()
print(x.thing) #"bdfbdfb"
y = TestClass(param="foo")
print(y.thing) #"foo"

This approach doesn't quite match the argument format in your question, since now param is a keyword-only argument, rather than a named positional argument. The principal practical difference is that you can't supply an argument for param unless you refer to it by name: z = TestClass("foo") won't do it, for example.


Based on the edits and comments to this question, another possible interpretation may be "How can I provide a parameter to a subclass that gets passed to the parent class, by any means necessary?", which has no requirement regarding default values. If you're willing to make param a mandatory parameter, then you simply need to pass the value in when creating a TestClass instance:

from unittest import TestCase

class TestCaseSubClass(TestCase):
    def __init__(self, param, *args, **kwargs):
        super().__init__(*args, **kwargs)  # Just use whatever is in TestCase's init + our stuff
        self.thing = param

class TestClass(TestCaseSubClass):
    def test_stuff(self):
        print('test stuff here')

x = TestClass("bdfbdfb")
print(x.thing) #"bdfbdfb"

Upvotes: 1

Related Questions