Skip to content

06 - Providing mutliple class constructors

  • Simulating multiple constructors with:
  • Optional arguments
  • Type checking
  • Write multiple constructors using @classmethod
  • Overload class constructors using @singledispatchmethod

Class Instantiation

The __new__ method

  • It is called before __init__.
  • It receives the class name and creates an instance of the class.
  • The instance is then usually passed to __init__ as its first parameter.
  • A simple rule of thumb is that the __new__ method should never have to be overridden unless you are subclassing an immutable Python data type such as numeric data types, strings, bytes, frozen sets, and tuples.

Defining multiple constructors

Optional arguments

class CumulativePowerFactory:
    class __init__(self, power=2, *, start=0):
        self.power = power
        self.total = start

    class __call__(self, base):
        value = base ** self.power
        self.total += value

        return value

# square = CumulativePowerFactory(2)
# cube = CumulativePowerFactory(3,start=100)

Checking the type with isinstance

class Person:
    def __init__(self, name, birth_date):
        self.name = name
        if isinstance(birth_date,date):
            self.birth_date = birth_date
        elif isinstance(birth_date,str):
            self.birth_date = datetime.fromisoformat(birth_date)
        else:
            raise ValueError(f"Unsupported type: {birth_date}")
Danger

This is an anti-pattern and it should be avoided.

Using the @classmethod

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @classmethod
    def from_diameter(cls, diameter):
        return cls(diameter/2)

    def area(self):
        return math.pi*self.radius**2

    def perimeter(self):
        return 2*math.pi*self.radius

# Circle.from_diameter(84)

Using @singledispatchmethod

  • It can be used on methods with single argument only. There are some non-standard libraries that allow you to use the dispatch technique in methods with multiple arguments. For example, multipledispatch
from functools import singledispatchmethod

class BirthInfo:
    @singledispatchmethod
    def __init__(self, birth_date):
        raise ValueError(f"Unsupported date format: {birth_date}")

    @__init__.register(date)
    def _from_date(self, birth_date):
        self.birth_date = birth_date

    @__init__.register()
    def _from_isoformat(self,birth_date: str):
        self.birth_date = date.fromisoformat(date)

    @__init__.register(int)
    @__init__.register(float)
    def _from_timestamp(self,birth_date):
        self.birth_date = date.fromtimestamp(birth_date)

Exercise

  • Review what class decorator does and how to implement your custom decorator.
  • Why it is said that dict is a cornerstone of CPython?
  • What are the differences between defaultDict and UserDict?
  • What are the underlying data structures used to implement the fundamental type sin Python?
  • Check the tutorial about pathlib.