Skip to content
GitLab
Explore
Sign in
Commits on Source (4)
Add order comparison operators
· c284b877
Dom Sekotill
authored
Aug 27, 2024
c284b877
Bump version for release 0.1.1
· d018002b
Dom Sekotill
authored
Aug 27, 2024
d018002b
Add a quick readme
· d62dcea2
Dom Sekotill
authored
Aug 27, 2024
d62dcea2
Add the readme to packages
· 2c7e59e5
Dom Sekotill
authored
Aug 27, 2024
2c7e59e5
Hide whitespace changes
Inline
Side-by-side
README.md
0 → 100644
View file @
2c7e59e5
Physical Quantities
===================
This package provides types for representing physical quantities, which have a magnitude and
a unit.
An in-depth usage guide is available in the module's docstring.
Quick Start
-----------
Create units by creating an enum class subclassing
`QuantityUnit`
. The value of each item
is scaled to its siblings, with an appropriate value chosen as the smallest representable
quantity which forms the base precision when scaling quantities. For example, units of
distance where the smallest possible quantity is 0.1mm:
```
python
from
kodo.quantities
import
QuantityUnit
class
Distance
(
QuantityUnit
):
MILLIMETERS
=
10
CENTIMETERS
=
10
*
MILLIMETERS
METERS
=
100
*
CENTIMETERS
KILOMETERS
=
1000
*
METERS
SIXTEENTH_INCH
=
15
# 1/16" == 1.5mm
QUARTER_INCH
=
4
*
SIXTEENTH_INCH
HALF_INCH
=
8
*
SIXTEENTH_INCH
INCH
=
16
*
SIXTEENTH_INCH
FOOT
=
12
*
INCH
class
Time
(
QuantityUnit
):
MILLISECONDS
=
1
SECONDS
=
1000
*
MILLISECONDS
MINUTES
=
60
*
SECONDS
```
Quantities are then created with the '@' operator, using a numeric value and the desired
unit, for example 3m:
```
python
from
kodo.quantities
import
Quantity
distance
:
Quantity
[
Distance
]
=
3
@
Distance
.
METERS
```
Quantities can be compared with, added to, and subtracted from other quantities of the same
type. Note that while use of different types of quantities together will not be caught at
run time, a type checker will report it as an error. The following examples use quantities
of the
`Distance`
type, so all work:
```
python
assert
(
3
@
Distance
.
METERS
)
==
(
300
@
Distance
.
CENTIMETERS
)
==
(
3000
@
Distance
.
MILLIMETERS
)
assert
(
3
@
Distance
.
METERS
)
+
(
2.5
@
Distance
.
METERS
)
==
(
5.5
@
Distance
.
METERS
)
```
Scaling with multiplication and division must be with unitless values:
```
python
assert
(
3
@
Distance
.
METERS
)
*
2.0
==
(
6
@
Distance
.
METERS
)
assert
(
3
@
Distance
.
METERS
)
/
2
==
(
1.5
@
Distance
.
METERS
)
```
Division with the floor division operator '//' and finding the remainder with the modulus
operator '%' work a little differently; the second argument must be another quantity:
```
python
# 10m divides into 3m three times…
assert
(
10
@
Distance
.
METERS
)
//
(
3
@
Distance
.
METERS
)
==
3
# … with 1m left over
assert
(
10
@
Distance
.
METERS
)
%
(
3
@
Distance
.
METERS
)
==
(
1
@
Distance
.
METERS
)
```
Finally, for most uses the quantity will eventually have to be converted back to a unitless
value of a known fixed unit, for example to pass to an external API, or store in a database.
This is done with the right shift operator '>>':
```
python
import
time
def
get_delay
()
->
Quantity
[
Time
]:
return
10000
@
Time
.
MILLISECONDS
# time.sleep() takes a single float argument for the sleep time in seconds
time
.
sleep
(
get_delay
()
>>
Time
.
SECONDS
)
```
If for some reason an integer value is needed instead, the floor division operator can be
used as described above with a value of 1 of the desired unit. As a convenience the unit
item itself can be used, matching the '>>' operator:
```
python
assert
(
3.6
@
Time
.
SECONDS
)
//
(
1
@
Time
.
SECONDS
)
is
3
assert
(
3.6
@
Time
.
SECONDS
)
//
Time
.
SECONDS
is
3
```
kodo/quantities/__init__.py
View file @
2c7e59e5
...
...
@@ -226,6 +226,12 @@ if TYPE_CHECKING:
# The remainder of a floor division with another quantity
def
__mod__
(
self
,
other
:
Quantity
[
U
],
/
)
->
Quantity
[
U
]:
...
# Quantities can be compared for order and equality
def
__lt__
(
self
,
other
:
Quantity
[
U
],
/
)
->
bool
:
...
def
__le__
(
self
,
other
:
Quantity
[
U
],
/
)
->
bool
:
...
def
__gt__
(
self
,
other
:
Quantity
[
U
],
/
)
->
bool
:
...
def
__ge__
(
self
,
other
:
Quantity
[
U
],
/
)
->
bool
:
...
else
:
# Although the runtime implementation does not use TypeVars, it is still generic to
# allow type subscripting.
...
...
pyproject.toml
View file @
2c7e59e5
...
...
@@ -8,9 +8,10 @@ email = "dom.sekotill@kodo.org.uk"
[project]
name
=
"kodo.quantities"
version
=
"0.1.
0
"
version
=
"0.1.
1
"
description
=
"Types for representing physical quantities, which have a magnitude and a unit"
license
=
{
file
=
"LICENCE.txt"
}
readme
=
"README.md"
classifiers
=
[
"Development Status :: 4 - Beta"
,
...
...
test.py
View file @
2c7e59e5
...
...
@@ -39,6 +39,18 @@ def test_modulus() -> None:
assert
(
3600
@
Time
.
MS
)
%
Time
.
S
==
600
@
Time
.
MS
def
test_order
()
->
None
:
assert
(
1
@
Time
.
S
)
>
(
999
@
Time
.
MS
)
assert
(
1
@
Time
.
S
)
>=
(
999
@
Time
.
MS
)
assert
(
1
@
Time
.
S
)
>=
(
1
@
Time
.
S
)
assert
(
999
@
Time
.
MS
)
<
(
1
@
Time
.
S
)
assert
(
999
@
Time
.
MS
)
<=
(
1
@
Time
.
S
)
assert
(
1
@
Time
.
S
)
<=
(
1
@
Time
.
S
)
assert
(
1
@
Time
.
S
)
!=
(
2
@
Time
.
S
)
if
TYPE_CHECKING
:
def
type_checks
()
->
None
:
# Checks for type checker false negatives
...
...
@@ -55,3 +67,8 @@ if TYPE_CHECKING:
_
=
(
1
@
Time
.
S
)
//
2
# type: ignore[operator]
_
=
(
1
@
Time
.
S
)
//
2.0
# type: ignore[operator]
_
=
(
1
@
Time
.
S
)
>
10
# type: ignore[operator]
_
=
(
1
@
Time
.
S
)
>=
10
# type: ignore[operator]
_
=
(
1
@
Time
.
S
)
<
10
# type: ignore[operator]
_
=
(
1
@
Time
.
S
)
<=
10
# type: ignore[operator]