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.
Example:
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