Thursday, 13 August 2015

Using *args and **kwargs : Python code post

In a previous post we looked at *args and **kwargs in different function calls. This post is about a practical use of these two in object oriented programming and inheritance.  Again, these can be used is to avoid having to write __init__ for every class that you create in a large set of small classes.

To avoid having to write __init__ for every class in the hierarchy a parent class can implement __init__ based on *args and **kwargs. Each child class that inherits this class can then be written without an __init__. Note that this makes the code or calling less intelligible for API users. For example,

class Parent(object):
    def __init__(self, *args, **kwargs):
        # loop through args (need names attributes) and kwargs to set attributes

class Child(object):
     pass

We can call the child as c = Child(name = 'Jason', age = 10). and so on. Notice that we do not see what all the Child takes in its constructor. To avoid this use the technique discussed below with named parameters for __init__.

With in a class we can define a __init__ which is the equivalent of a constructor in Python. For a basic class which has a fixed set of arguments and is not present in a hierarchy this can be straight forward. In a hierarchy of classes a child class's __init__ could be called with a number of arguments, some of which can be inherited attributes from the parent class. In such a scenario the __init__ can use named parameters for its own attributes and then user *args or **kwargs to pass on the rest of the attributes to the parent class. The parent class can build its attributes from these and if there are any extras it can pass them to the grand parent and so on.

Here we look at an example hierarchy. Items -> RetailItems. RetailItems->FoodItems. RetailItems -> ElectronicAccessories. The code for the base class shown below. *args and **kwargs are used to build the instance in __init__. To process args we need to know names of attributes to assign to each value in args. We can use a list as shown below.


A RetailItem which derieves from Item and a FoodItem are shown below.


FoodItem can then be called as shown below. Notice than when we code up a FoodItem we are not in a position to know the attributes except to lookup the class for _expected_attributes or the parent class. As a advantage this saves time taken to write up an __init__.


To make the API more developer friendly we can use name parameters for __init__. So __init__ in the child class will have parameters of its own in addition to *args and **kwargs. To know what else goes in to the parent class we still need to look at the Parent class documentation. However the child class __init__ can take a variable number of named parameters. These parameters will then be handled by parent classed as needed. This is a usage scenario for *args and **kwargs. Notice that there is no expected list of attributes.


To make sure developers know how to build the object we can use the _expected_attributes. However the attributes other than specifications is handled by parent class (thanks to *args and **kwargs).

Code is available here

No comments: