Defining FixedPoint initialization

We'll start with initialization, which includes conversions of various types to the FixedPoint values as follows:

import numbers
import math
from typing import Union, Optional, Any

class FixedPoint(numbers.Rational):
__slots__ = ("value", "scale", "default_format")

def __init__(self, value: Union['FixedPoint', int, float], scale: int = 100) -> None:
self.value: int
self.scale: int
if isinstance(value, FixedPoint):
self.value = value.value
self.scale = value.scale
elif isinstance(value, int):
self.value = value
self.scale = scale
elif isinstance(value, float):
self.value = int(scale * value + .5) # Round half up
self.scale = scale
else:
raise TypeError(f"Can't build FixedPoint from {value!r} of {type(value)}")
digits = int(math.log10(scale))
self.default_format = "{{0:.{digits}f}}".format(digits=digits)

def __str__(self) -> str:
return self.__format__(self.default_format)

def __repr__(self) -> str:
return f"{self.__class__.__name__:s}({self.value:d},scale={self.scale:d})"

def __format__(self, specification: str) -> str:
if specification == "":
specification = self.default_format
return specification.format(self.value / self.scale)

Our FixedPoint class is defined as a numbers.Rational subclass. We're going to wrap two integer values, scale and value, and follow the general definitions for fractions. This requires a large number of special method definitions. The initialization is for an immutable object, so it overrides __new__() instead of __init__(). It defines a limited number of slots to prevent the adding of any further attributes. The initialization includes several kinds of conversions as follows:

  • If we're given another FixedPoint object, we'll copy the internal attributes to create a new FixedPoint object that's a kind of clone of the original. It will have a unique ID, but we can be sure it has the same hash value and compares as equal, making the clone largely indistinguishable.
  • When given integral or rational values (concrete classes of int or float), these are used to set the value and scale attributes.
  • We can add cases to handle decimal.Decimal and fractions.Fraction, as well as parsing input string values.

We've defined three special methods to produce string results: __str__(),
__repr__(), and __format__(). For the format operation, we've decided to leverage the existing floating-point features of the format specification language. Because this is a rational number, we need to provide numerator and denominator methods.

Note that we could have also started with wrapping the existing fractions.Fraction class. In order to show more of the programming required, we opted to start from the abstract Rational class.

Let's see how to define FixedPoint binary arithmetic operators in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset