Multiple constructors in a Python class

In addition to working with Python, I also work with Java quite a lot.

When coding in Python, I occasionally encounter situations in which I wish I could code multiple constructors  (with different signatures)  for a class, the way you can in Java.  

Recently, someone else had the same desire, and posted his question on comp.lang.python. So I thought that I would post an example of the technique that I use, in case others might find it useful.  So here it is:

==========================================

 
import sys, types, pprint

class Vector:
    """
    Demo of a class with multiple signatures for the constructor
    """
    def __init__(self, *args, **kwargs):
        
        if len(args) == 1:  foundOneArg = True;  theOnlyArg = args[0]
        else:               foundOneArg = False; theOnlyArg  = None

        if foundOneArg and isinstance(theOnlyArg, types.ListType):      
            self.initializeFromList(theOnlyArg)             
        elif foundOneArg and isinstance(theOnlyArg,Vector):
            self.initializeFromVector(theOnlyArg)           
        else:
            self.initializeFromArgs(*args)

        pprint.pprint(self.values)  # for debugging only
        
    def initializeFromList(self, argList):
        self.values = [x for x in argList]

    def initializeFromVector(self, vector):
        self.values = [x for x in vector.values]

    def initializeFromArgs(self, *args):
        self.values = [x for x in args]
#------------ end of class definition ---------------------

v = Vector(1,2,3) 
v = Vector([4,5,6]) 
q = Vector(v);
About these ads

6 thoughts on “Multiple constructors in a Python class

  1. I’d almost be inclined to make an automated procedure for doing the arg checking, and use a decorator to declare individual methods as constructors.

    I don’t have the code on me at the moment, so you’ll have to leave it at that for the moment.

  2. I personally never was a fan of such a design- specifically relying on *args/**kwargs means in looking at the prototype you’ve nfc what args it actually takes, forcing you to access the doc string for it (instead of just doing a quick inspection of it).

    What I do instead, and I think is cleaner/simpler is to invert what you’ve got there- instead of __init__ trying to figure out which internal initialization to invoke, I use classmethods to translate the various init signatures into the common __init__ signature.

    Benefits of this is 1) explicit intentions, 2) simpler internal init, 3) usable method signatures, 4) easier to subclass/override (think of if you needed to change the prototype signature for one of the subinits- your current code, __init__ would have to basically be reimplemented in the derivative).

    Also reads a fair bit better in invoking code in my experience.

  3. This example is a little trivial, so it’s hard to really demonstrate good examples. But usually I use a classmethod for alternate constructors. For example: http://gist.github.com/335850

    Otherwise I sometimes use keyword arguments, which are explicit about what kind of thing is being passed in. Doing full type inspection of constructor arguments is hard (or it is sloppy).

  4. I’ll just echo what Ian said: I tend to use classmethods with names like “from_list”, “from_vector” whose responsibility it is to invoke __init__ in a sensible manner.

  5. Here’s my stab at a prettier version:

    class Vector:
        """
        Demo of a class with multiple signatures for the constructor
        """
        def __init__(self, first=[], *rest, **kwargs):
            if rest:
                self.values = [first]
            else:
                self.values = []
                rest = first
     
            self.values.extend(list(rest))
     
            if __debug__:
                pprint.pprint(self.values)
     
        def __iter__(self):
            for elem in self.values:
                yield elem
     
    #------------ end of class definition ---------------------
    
    v = Vector() 
    v = Vector(1,2,3)
    v = Vector([4,5,6])
    q = Vector(v);
    r = Vector(v,[v],v)
    
    • A mutable object in a definition… really? return iter(self.values) in __iter__ would also be a better solution.

Comments are closed.