What is a clean, Pythonic way to have multiple constructors in Python? What is a clean, Pythonic way to have multiple constructors in Python? python python

What is a clean, Pythonic way to have multiple constructors in Python?

Actually None is much better for "magic" values:

class Cheese():    def __init__(self, num_holes = None):        if num_holes is None:            ...

Now if you want complete freedom of adding more parameters:

class Cheese():    def __init__(self, *args, **kwargs):        #args -- tuple of anonymous arguments        #kwargs -- dictionary of named arguments        self.num_holes = kwargs.get('num_holes',random_holes())

To better explain the concept of *args and **kwargs (you can actually change these names):

def f(*args, **kwargs):   print 'args: ', args, ' kwargs: ', kwargs>>> f('a')args:  ('a',)  kwargs:  {}>>> f(ar='a')args:  ()  kwargs:  {'ar': 'a'}>>> f(1,2,param=3)args:  (1, 2)  kwargs:  {'param': 3}


Using num_holes=None as the default is fine if you are going to have just __init__.

If you want multiple, independent "constructors", you can provide these as class methods. These are usually called factory methods. In this case you could have the default for num_holes be 0.

class Cheese(object):    def __init__(self, num_holes=0):        "defaults to a solid cheese"        self.number_of_holes = num_holes    @classmethod    def random(cls):        return cls(randint(0, 100))    @classmethod    def slightly_holey(cls):        return cls(randint(0, 33))    @classmethod    def very_holey(cls):        return cls(randint(66, 100))

Now create object like this:

gouda = Cheese()emmentaler = Cheese.random()leerdammer = Cheese.slightly_holey()

One should definitely prefer the solutions already posted, but since no one mentioned this solution yet, I think it is worth mentioning for completeness.

The @classmethod approach can be modified to provide an alternative constructor which does not invoke the default constructor (__init__). Instead, an instance is created using __new__.

This could be used if the type of initialization cannot be selected based on the type of the constructor argument, and the constructors do not share code.


class MyClass(set):    def __init__(self, filename):        self._value = load_from_file(filename)    @classmethod    def from_somewhere(cls, somename):        obj = cls.__new__(cls)  # Does not call __init__        super(MyClass, obj).__init__()  # Don't forget to call any polymorphic base class initializers        obj._value = load_from_somewhere(somename)        return obj