Python Conquers The Universe

2010/03/17

Multiple constructors in a Python class

Filed under: Python features — Steve Ferg @ 3:45 pm

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);

6 Comments »

  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.

    Comment by Christopher Neugebauer — 2010/03/17 @ 6:14 pm | Reply

  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.

    Comment by Brian Harring — 2010/03/17 @ 7:20 pm | Reply

  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).

    Comment by Ian Bicking — 2010/03/17 @ 7:23 pm | Reply

  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.

    Comment by Richard Jones — 2010/03/17 @ 9:03 pm | Reply

  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)
    

    Comment by Tso — 2010/03/18 @ 2:50 am | Reply

    • A mutable object in a definition… really? return iter(self.values) in __iter__ would also be a better solution.

      Comment by DasIch — 2010/03/18 @ 9:47 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 48 other followers