Subclassing Immutable Built-in Types

Copied!
Happy Pythoning!

If you’re interested in converting units, then you can check out Pint.

raaki88

Hi Darren,

Thank you for the course, one question , you did say that new will accept only one argument for class, how come we can pass a value as well in this case ?

praghavan1973

In response to question from raaki88:

I think the new for float accepts only one argument which is the value for float. For the new subclass we are creating we can accept depending on our definition of new.

mochibeepboop

Why do we need to overrride new just to create a simple class that calculates distance?

Bartosz Zaczyński RP Team

@mochibeepboop Strictly speaking, you don’t have to override the `.__new__()` method to implement a class that calculates distance. For example, you could’ve implemented the same logic as a data class, avoiding inheritance and many challenges associated with it:

``````>>> from dataclasses import dataclass

>>> @dataclass
... class Distance:
...     value: float
...     unit: str
...
...         if isinstance(other, Distance):
...             if self.unit == other.unit:
...                 return Distance(self.value + other.value, self.unit)
...         raise NotImplementedError
...
>>> d1 = Distance(42.0, "miles")
>>> d2 = Distance(8.0, "miles")

>>> d1 + d2
Distance(value=50.0, unit='miles')
``````

Darren’s code serves as an example to demonstrate how you can subclass a built-in immutable data type, such as `float`. Notice that floating-point numbers take one argument—the value—and you want your `Distance` class to take an additional unit argument. When you override `.__init__()` by adding this extra argument, instantiating the class results in an error:

``````>>> class Distance(float):
...     def __init__(self, value, unit):
...         super().__init__(value)
...         self.unit = unit
...
>>> Distance(42.0, "miles")
Traceback (most recent call last):
File "<input>", line 1, in <module>
Distance(42.0, "miles")
TypeError: float expected at most 1 argument, got 2
``````

That’s because `super().__init__()` still expects only the `value` argument. Therefore, you must override the `.__new__()` method in such a way that it accepts both the `value` and `unit` arguments but only passes the value to `super().__new__()`:

``````>>> class Distance(float):
...     def __new__(cls, value, unit):
...         instance = super().__new__(cls, value)
...         instance.unit = unit
...         return instance
...
>>> Distance(42.0, "miles")
42.0
>>> distance = Distance(42.0, "miles")
>>> distance.unit
'miles'
``````

to join the conversation.