|
25 | 25 |
|
26 | 26 |
|
27 | 27 | class Quantity: |
28 | | - """A quantity with a unit.""" |
| 28 | + """A quantity with a unit. |
| 29 | +
|
| 30 | + Quantities try to behave like float and are also immutable. |
| 31 | + """ |
29 | 32 |
|
30 | 33 | _base_value: float |
31 | 34 | """The value of this quantity in the base unit.""" |
@@ -63,6 +66,30 @@ def __init_subclass__(cls, exponent_unit_map: dict[int, str]) -> None: |
63 | 66 | cls._exponent_unit_map = exponent_unit_map |
64 | 67 | super().__init_subclass__() |
65 | 68 |
|
| 69 | + _zero_cache: dict[type, Quantity] = {} |
| 70 | + """Cache for zero singletons. |
| 71 | +
|
| 72 | + This is a workaround for mypy getting confused when using @functools.cache and |
| 73 | + @classmethod combined with returning Self. It believes the resulting type of this |
| 74 | + method is Self and complains that members of the actual class don't exist in Self, |
| 75 | + so we need to implement the cache ourselves. |
| 76 | + """ |
| 77 | + |
| 78 | + @classmethod |
| 79 | + def zero(cls) -> Self: |
| 80 | + """Return a quantity with value 0. |
| 81 | +
|
| 82 | + Returns: |
| 83 | + A quantity with value 0. |
| 84 | + """ |
| 85 | + _zero = cls._zero_cache.get(cls, None) |
| 86 | + if _zero is None: |
| 87 | + _zero = cls.__new__(cls) |
| 88 | + _zero._base_value = 0 |
| 89 | + cls._zero_cache[cls] = _zero |
| 90 | + assert isinstance(_zero, cls) |
| 91 | + return _zero |
| 92 | + |
66 | 93 | @property |
67 | 94 | def base_value(self) -> float: |
68 | 95 | """Return the value of this quantity in the base unit. |
@@ -250,48 +277,6 @@ def __mul__(self, percent: Percentage) -> Self: |
250 | 277 | product._base_value = self._base_value * percent.as_fraction() |
251 | 278 | return product |
252 | 279 |
|
253 | | - def __iadd__(self, other: Self) -> Self: |
254 | | - """Add another quantity to this one. |
255 | | -
|
256 | | - Args: |
257 | | - other: The other quantity. |
258 | | -
|
259 | | - Returns: |
260 | | - This quantity. |
261 | | - """ |
262 | | - if not type(other) is type(self): |
263 | | - return NotImplemented |
264 | | - self._base_value += other._base_value |
265 | | - return self |
266 | | - |
267 | | - def __isub__(self, other: Self) -> Self: |
268 | | - """Subtract another quantity from this one. |
269 | | -
|
270 | | - Args: |
271 | | - other: The other quantity. |
272 | | -
|
273 | | - Returns: |
274 | | - This quantity. |
275 | | - """ |
276 | | - if not type(other) is type(self): |
277 | | - return NotImplemented |
278 | | - self._base_value -= other._base_value |
279 | | - return self |
280 | | - |
281 | | - def __imul__(self, percent: Percentage) -> Self: |
282 | | - """Multiply this quantity by a percentage. |
283 | | -
|
284 | | - Args: |
285 | | - percent: The percentage. |
286 | | -
|
287 | | - Returns: |
288 | | - This quantity. |
289 | | - """ |
290 | | - if not isinstance(percent, Percentage): |
291 | | - return NotImplemented |
292 | | - self._base_value *= percent.as_fraction() |
293 | | - return self |
294 | | - |
295 | 280 | def __gt__(self, other: Self) -> bool: |
296 | 281 | """Return whether this quantity is greater than another. |
297 | 282 |
|
@@ -446,7 +431,7 @@ class Power( |
446 | 431 | ): |
447 | 432 | """A power quantity. |
448 | 433 |
|
449 | | - Objects of this type are wrappers around `float` values. |
| 434 | + Objects of this type are wrappers around `float` values and are immutable. |
450 | 435 |
|
451 | 436 | The constructors accept a single `float` value, the `as_*()` methods return a |
452 | 437 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -619,7 +604,7 @@ class Current( |
619 | 604 | ): |
620 | 605 | """A current quantity. |
621 | 606 |
|
622 | | - Objects of this type are wrappers around `float` values. |
| 607 | + Objects of this type are wrappers around `float` values and are immutable. |
623 | 608 |
|
624 | 609 | The constructors accept a single `float` value, the `as_*()` methods return a |
625 | 610 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -715,7 +700,7 @@ class Voltage( |
715 | 700 | ): |
716 | 701 | """A voltage quantity. |
717 | 702 |
|
718 | | - Objects of this type are wrappers around `float` values. |
| 703 | + Objects of this type are wrappers around `float` values and are immutable. |
719 | 704 |
|
720 | 705 | The constructors accept a single `float` value, the `as_*()` methods return a |
721 | 706 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -837,7 +822,7 @@ class Energy( |
837 | 822 | ): |
838 | 823 | """An energy quantity. |
839 | 824 |
|
840 | | - Objects of this type are wrappers around `float` values. |
| 825 | + Objects of this type are wrappers around `float` values and are immutable. |
841 | 826 |
|
842 | 827 | The constructors accept a single `float` value, the `as_*()` methods return a |
843 | 828 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -956,7 +941,7 @@ class Frequency( |
956 | 941 | ): |
957 | 942 | """A frequency quantity. |
958 | 943 |
|
959 | | - Objects of this type are wrappers around `float` values. |
| 944 | + Objects of this type are wrappers around `float` values and are immutable. |
960 | 945 |
|
961 | 946 | The constructors accept a single `float` value, the `as_*()` methods return a |
962 | 947 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -1069,7 +1054,7 @@ class Percentage( |
1069 | 1054 | ): |
1070 | 1055 | """A percentage quantity. |
1071 | 1056 |
|
1072 | | - Objects of this type are wrappers around `float` values. |
| 1057 | + Objects of this type are wrappers around `float` values and are immutable. |
1073 | 1058 |
|
1074 | 1059 | The constructors accept a single `float` value, the `as_*()` methods return a |
1075 | 1060 | `float` value, and each of the arithmetic operators supported by this type are |
|
0 commit comments