|
24 | 24 |
|
25 | 25 |
|
26 | 26 | class Quantity: |
27 | | - """A quantity with a unit.""" |
| 27 | + """A quantity with a unit. |
| 28 | +
|
| 29 | + Quantities try to behave like float and are also immutable. |
| 30 | + """ |
28 | 31 |
|
29 | 32 | _base_value: float |
30 | 33 | """The value of this quantity in the base unit.""" |
@@ -62,6 +65,30 @@ def __init_subclass__(cls, exponent_unit_map: dict[int, str]) -> None: |
62 | 65 | cls._exponent_unit_map = exponent_unit_map |
63 | 66 | super().__init_subclass__() |
64 | 67 |
|
| 68 | + _zero_cache: dict[type, Quantity] = {} |
| 69 | + """Cache for zero singletons. |
| 70 | +
|
| 71 | + This is a workaround for mypy getting confused when using @functools.cache and |
| 72 | + @classmethod combined with returning Self. It believes the resulting type of this |
| 73 | + method is Self and complains that members of the actual class don't exist in Self, |
| 74 | + so we need to implement the cache ourselves. |
| 75 | + """ |
| 76 | + |
| 77 | + @classmethod |
| 78 | + def zero(cls) -> Self: |
| 79 | + """Return a quantity with value 0. |
| 80 | +
|
| 81 | + Returns: |
| 82 | + A quantity with value 0. |
| 83 | + """ |
| 84 | + _zero = cls._zero_cache.get(cls, None) |
| 85 | + if _zero is None: |
| 86 | + _zero = cls.__new__(cls) |
| 87 | + _zero._base_value = 0 |
| 88 | + cls._zero_cache[cls] = _zero |
| 89 | + assert isinstance(_zero, cls) |
| 90 | + return _zero |
| 91 | + |
65 | 92 | @property |
66 | 93 | def base_value(self) -> float: |
67 | 94 | """Return the value of this quantity in the base unit. |
@@ -249,48 +276,6 @@ def __mul__(self, percent: Percentage) -> Self: |
249 | 276 | product._base_value = self._base_value * percent.as_fraction() |
250 | 277 | return product |
251 | 278 |
|
252 | | - def __iadd__(self, other: Self) -> Self: |
253 | | - """Add another quantity to this one. |
254 | | -
|
255 | | - Args: |
256 | | - other: The other quantity. |
257 | | -
|
258 | | - Returns: |
259 | | - This quantity. |
260 | | - """ |
261 | | - if not type(other) is type(self): |
262 | | - return NotImplemented |
263 | | - self._base_value += other._base_value |
264 | | - return self |
265 | | - |
266 | | - def __isub__(self, other: Self) -> Self: |
267 | | - """Subtract another quantity from this one. |
268 | | -
|
269 | | - Args: |
270 | | - other: The other quantity. |
271 | | -
|
272 | | - Returns: |
273 | | - This quantity. |
274 | | - """ |
275 | | - if not type(other) is type(self): |
276 | | - return NotImplemented |
277 | | - self._base_value -= other._base_value |
278 | | - return self |
279 | | - |
280 | | - def __imul__(self, percent: Percentage) -> Self: |
281 | | - """Multiply this quantity by a percentage. |
282 | | -
|
283 | | - Args: |
284 | | - percent: The percentage. |
285 | | -
|
286 | | - Returns: |
287 | | - This quantity. |
288 | | - """ |
289 | | - if not isinstance(percent, Percentage): |
290 | | - return NotImplemented |
291 | | - self._base_value *= percent.as_fraction() |
292 | | - return self |
293 | | - |
294 | 279 | def __gt__(self, other: Self) -> bool: |
295 | 280 | """Return whether this quantity is greater than another. |
296 | 281 |
|
@@ -413,7 +398,7 @@ class Power( |
413 | 398 | ): |
414 | 399 | """A power quantity. |
415 | 400 |
|
416 | | - Objects of this type are wrappers around `float` values. |
| 401 | + Objects of this type are wrappers around `float` values and are immutable. |
417 | 402 |
|
418 | 403 | The constructors accept a single `float` value, the `as_*()` methods return a |
419 | 404 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -586,7 +571,7 @@ class Current( |
586 | 571 | ): |
587 | 572 | """A current quantity. |
588 | 573 |
|
589 | | - Objects of this type are wrappers around `float` values. |
| 574 | + Objects of this type are wrappers around `float` values and are immutable. |
590 | 575 |
|
591 | 576 | The constructors accept a single `float` value, the `as_*()` methods return a |
592 | 577 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -682,7 +667,7 @@ class Voltage( |
682 | 667 | ): |
683 | 668 | """A voltage quantity. |
684 | 669 |
|
685 | | - Objects of this type are wrappers around `float` values. |
| 670 | + Objects of this type are wrappers around `float` values and are immutable. |
686 | 671 |
|
687 | 672 | The constructors accept a single `float` value, the `as_*()` methods return a |
688 | 673 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -804,7 +789,7 @@ class Energy( |
804 | 789 | ): |
805 | 790 | """An energy quantity. |
806 | 791 |
|
807 | | - Objects of this type are wrappers around `float` values. |
| 792 | + Objects of this type are wrappers around `float` values and are immutable. |
808 | 793 |
|
809 | 794 | The constructors accept a single `float` value, the `as_*()` methods return a |
810 | 795 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -923,7 +908,7 @@ class Frequency( |
923 | 908 | ): |
924 | 909 | """A frequency quantity. |
925 | 910 |
|
926 | | - Objects of this type are wrappers around `float` values. |
| 911 | + Objects of this type are wrappers around `float` values and are immutable. |
927 | 912 |
|
928 | 913 | The constructors accept a single `float` value, the `as_*()` methods return a |
929 | 914 | `float` value, and each of the arithmetic operators supported by this type are |
@@ -1036,7 +1021,7 @@ class Percentage( |
1036 | 1021 | ): |
1037 | 1022 | """A percentage quantity. |
1038 | 1023 |
|
1039 | | - Objects of this type are wrappers around `float` values. |
| 1024 | + Objects of this type are wrappers around `float` values and are immutable. |
1040 | 1025 |
|
1041 | 1026 | The constructors accept a single `float` value, the `as_*()` methods return a |
1042 | 1027 | `float` value, and each of the arithmetic operators supported by this type are |
|
0 commit comments