Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

self.__class__ in parent class' method is missing arguments

A package I am using contains the class A. I want my new class B to inherit from this class. Minimal example:

class A():
    def __init__(self,name):
        self.name = name
    def my_method(self):
        return self.__class__(name = self.name)

class B(A):
    def __init__(self, value, name):
        self.value = value
        super().__init__(name)

B_instance = B(value = 5, name = "Bob")
B_instance.my_method()

Calling the parent’s method on the child throws:

File ..., line 5, in my_method
    return self.__class__(name = self.name)
TypeError: __init__() missing 1 required positional argument: 'value'

How can I fix this without changing anything in class A? Many thanks for your help!

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

A.my_method assumes that A (or any subclass thereof) can be instantiated with a single argument. You broke that assumption by adding a required argument value to B.__init__. You’ll need to make value the second parameter, and make it optional.

class B(A):
    def __init__(self, name, value=None):
        super().__init__(name)
        self.value = value

Whether it is useful to use A.my_method to create an instance of B with value=None is another question.


Let’s pretend you could change A. Then it would be better to design it to facilitate subclassing in the first place.

class A:
    def __init__(self, *, name, **kwargs):
        super().__init__(**kwargs)
        self.name = name

    @classmethod
    def my_method(cls, **kwargs):
        # Note: passing an explicit name argument will
        # result in a TypeError on multiple arguments for
        # the keyword argument 'name'
        return cls(name="Alice", **kwargs)

Now you could define B as

class B(A):
    def __init__(self, *, value, **kwargs):
        super().__init__(**kwargs)
        self.value = value


a1 = A(name="Bob")
a2 = A.my_method()  # A(name="Alice")

b1 = B(name="Joe", value=5)
b2 = B.my_method(value=10)  # B(name="Alice", value=10)

This uses advice from https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ with regards to __init__ (and by extension, alternate class-method constructors):

  • use keyword arguments when calling
  • only handle your own parameters explicitly
  • accept all unexpected keyword arguments to pass on to an ancestor class
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading