3 # © Copyright 2021-2022, Scott Gasch
5 """A class to represent a rate of change."""
7 from typing import Optional
11 """A class to represent a rate of change."""
15 multiplier: Optional[float] = None,
17 percentage: Optional[float] = None,
18 percent_change: Optional[float] = None,
20 """Constructs a new :class:`Rate` from a multiplier, percentage, or
21 percent change. One and only one of these may be passed. These are
22 a little confusing so here's an example...
26 A multiplier of 1.5x is the same as a percentage of 150% and
27 is also the same as a 50% change. Let's examine an original
28 amount of 100. Multiplying it by a 1.5x multiplier yields 150.
29 Multiplying it by 150% yields 150. Increasing it by 50% also
33 multiplier: provides the number that you would multiply a base
34 amount by to modify it
35 percentage: provides the multiplier as a percentage
36 percent_change: provides the multiplier as a percent change to
40 if multiplier is not None:
41 if isinstance(multiplier, str):
42 multiplier = multiplier.replace("%", "")
45 self.multiplier: float = m
47 self.multiplier = multiplier
49 if percentage is not None:
50 self.multiplier = percentage / 100
52 if percent_change is not None:
53 self.multiplier = 1.0 + percent_change / 100
57 "Exactly one of percentage, percent_change or multiplier is required."
60 def apply_to(self, other):
61 """Applies the rate to a base number.
64 other: the base to apply the change rate to.
67 The result after the change.
69 return self.__mul__(other)
72 """Applies the rate to a base number.
75 other: the base to apply the change rate to.
78 The result after the change.
80 return self.__mul__(other)
83 return self.multiplier
85 def __mul__(self, other):
86 return self.multiplier * float(other)
90 def __truediv__(self, other):
91 return self.multiplier / float(other)
93 def __add__(self, other):
94 return self.multiplier + float(other)
98 def __sub__(self, other):
99 return self.multiplier - float(other)
101 def __eq__(self, other):
102 return self.multiplier == float(other)
104 def __ne__(self, other):
105 return not self.__eq__(other)
107 def __lt__(self, other):
108 return self.multiplier < float(other)
110 def __gt__(self, other):
111 return self.multiplier > float(other)
113 def __le__(self, other):
114 return self < other or self == other
116 def __ge__(self, other):
117 return self > other or self == other
120 return self.multiplier
122 def __repr__(self, *, relative=False, places=3):
124 percentage = (self.multiplier - 1.0) * 100.0
126 percentage = self.multiplier * 100.0
127 return f"{percentage:+.{places}f}%"