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.