Typing the numbers module

I’m not sure whether this fits in Typing or in Ideas or in Help.

See numbers module · Issue #11863 · python/typeshed · GitHub.
Basically, the mathematical rule that a rational number divided by a rational number gives a rational number, is not described in the typing of that operation in the stdlib numbers module.
It may make sense to do what I propose in that thread, which is to have overload signatures like the following (pseudotypes like _ComplexLike being simplified to Complex):

class Complex:
    def __truediv__(self, other: Complex) -> Complex: ...

...

class Rational(Real):
    def __truediv__(self, other: Rational) -> Rational: ...
    @overload
    def __truediv__(self, other: Real) -> Real: ...
    @overload
    def __truediv__(self, other: Complex) -> Complex: ...

That would certainly clear some type warnings I’m getting on my project.

However, would it be a constraint on the liberty officially permitted by the numbers module and related PEP ? In other words, should it be considered legal to make a subclass of Rational which when divided by a rational may give a complex ?

1 Like

I would say no. These number types are supposed to represent the more abstract mathematical sets [1], which have well defined operations between them. ℚ divide by ℚ is well defined to be within ℚ, and definitely not part of ℂ.

However, division by 0 throws a bit of wrench here. I would still say that keeping within ℚ is the best behavior, even if your extend your definition of ℚ to including a representation for infinity (like floating point numbers do).

These classes already imply extra restrictions on the behavior of these classes. For example, I would be annoyed if I get instances of these interfaces where I can’t assume communicative and associativity within a reasonable approximation (i.e. floats are fine)


  1. not quite the correct term to use ↩︎

There’s been a lot of discussion and attempts to make the numbers module work well with static typing (int is not a Number? · Issue #3186 · python/mypy · GitHub, previous typeshed PRs like int is not a Number? · Issue #3186 · python/mypy · GitHub and Further improve return types in the `numbers` module by AlexWaygood · Pull Request #11375 · python/typeshed · GitHub). My conclusion is that you should not use the numbers module with static typing and attempts to do so are likely to run into trouble.

Your proposed annotation for Rational.__truediv__ makes the class impossible to implement. These annotations would imply that an implementation of Complex needs to accept any other implementation of Complex in __truediv__, but how could a Complex type in one library know about a different Complex type in another and know how to handle it?

2 Likes

It would use the real and imag properties which are part of the Complex API to convert the value to the local Complex type.

Those properties return more numbers classes (“_RealLike” in typeshed), which means there’s still no guarantee you can convert them to a class your library knows how to deal with.

Quoting the numbers doc page :

class numbers.Complex

Subclasses of this type describe complex numbers and include […] conversions to complex

class numbers.Real

To Complex, Real adds the operations that work on real numbers.

In short, those are: a conversion to float

class numbers.Integral

Subtypes Rational and adds a conversion to int.

Since conversion to the native types is provided, then all an implem has to provide is a conversion from the native types (it doesn’t even have to be part of the public API) in order to support every other implem.
If not, then they are not complying with the numbers API and while ignoring such edge cases is fine in my book, errors should not pass silently unless explicitly silenced ; in other words, a type # ignore is deserved for implementation that would not make the effort to support that.