Search code examples
pythonpython-internals

Reduce boilerplate from __init__ using Inheritance


I am trying to inject constructor i.e. __init__ via super class, to avoid boilerplate code in __init__ in all my domain classes.

For example:

class Structure:
    _fields = []

    def __init__(self, *args):
        if len(args) != len(self._fields):
            raise TypeError("Wrong # arguments")
        for name, value in zip(self._fields, args):
            setattr(self, name, value)

class Stock(Structure):
    _fields = ['name', 'shares', 'price']

stock = Stock("Amzn", "11", "2100")
print(stock.name)

The code above works fine when the constructor is limited to *args. But there are some domain classes that takes **kwargs too.

For example something like below:

class Structure:
    _fields = []

    def __init__(self, *args, **kwargs):
        if (len(args) + len(kwargs)) != len(self._fields):
            raise TypeError("Wrong # arguments")

        for name, value in zip(self._fields, args):
            setattr(self, name, value)

class Stock(Structure):
    _fields = ['name', 'shares', 'price']

stock = Stock("Amzn", "11", price = "2100")
stock.price #AttributeError, stock object has no attribute 'price'

But obviously the code above won't set the kwargs, because I never touched kwargs in __init__. Any idea how can I get this fixed?


Solution

  • How about checking if kwargs exists?

    >>> class SC: 
    ...:     _fields = [] 
    ...:     def __init__(self, *args, **kwargs):
    ...:         if (len(args) + len(kwargs)) != len(self._fields):
    ...:              raise TypeError("Wrong # arguments") 
    ...:         for name, value in zip(self._fields, args): 
    ...:             setattr(self, name, value) 
    ...:         if kwargs: 
    ...:             self.__dict__.update(kwargs) 
    ...:                                                                                                                                                                                      
    
    >>> class SD2(SC): 
    ...     _fields = ['name', 'shares', 'price'] 
    
    
    >>> i = SD2(name='Amzn', shares=1, price=2)                                                                                                                                              
    >>> i.name                                                                                                                                                                               
    'Amzn'
    >>> i.shares  
    1
    

    This works like this too:

    >>>u= SD2('Amzn', shares=1, price=2)                                                                                                                                                             
    >>>u.name                                                                                                                                                                                
    'Amzn'