[Python-ideas] Units in type hints

Chris Angelico rosuav at gmail.com
Fri May 15 17:28:36 CEST 2015


On Sat, May 16, 2015 at 12:00 AM, Koos Zevenhoven
<koos.zevenhoven at aalto.fi> wrote:
> On 14.5.2015 14:59, Steven D'Aprano wrote:
>> But that's not an error. Calling sleep(weight_in_kilograms) is an error.
>
> In the example I gave, it is clearly an error. And it would be an error with
> time.sleep. But you are obviously right, sleeping for kilograms is also an
> error, although a very bizarre one.

I dunno, maybe you're a heavy sleeper? :)

>> If the user has to do the conversion themselves,
>> that's a source of error:
>>
>> sleep(time_in_milliseconds / 1000)  # convert to seconds
>>
>> If you think that's too obvious an error for anyone to make,
>
> You lost me now. There does not seem to be an error in the line of code you
> provided, especially not when using Python 3, which has true division by
> default. However, in what I proposed, the type checker would complain
> because you made a manual conversion without changing the unit hint (which
> is also potential source of error, and you seem to agree).

Dividing a unit-aware value by a scalar shouldn't be an error. "I have
an A4 sheet of paper. If I fold it in half seven times, how big will
it be?" => 210mm*297mm/(2**7) == 487.265625 mm^2.

The unit would simply stay the same after the division; what you'd
have is the thousandth part of the time, still in milliseconds. If you
have a typing system that's unit-aware, this would still be an error,
but it would be an error because you're still giving milliseconds to a
function that wants seconds.

It'd possibly be best to have actual real types for your unit-aware
values. Something like:

class UnitAware:
    def __init__(self, value: float, unit: str):
        self.value = value
        self.unit = unit
    def __mul__(self, other):
        if isinstance(other, UnitAware):
            # perform compatibility/conversion checks
        else: return UnitAware(self.value * other, self.unit)
    # etc
    def as(self, unit):
        # attempt to convert this value into the other unit

Then you could have hinting types that stipulate specific units:

class Unit(str):
    def __instancecheck__(self, val):
        return isinstance(val, UnitAware) and val.unit == self
ms = Unit("ms")
sec = Unit("sec")
m = Unit("m")

This would allow you to go a lot further than just type hints. But
maybe this would defeat the purpose, in that it'd have to have every
caller and callee aware that they're looking for a unit-aware value
rather than a raw number - so it wouldn't be easy to deploy
backward-compatibly.

ChrisA


More information about the Python-ideas mailing list