Skip to content

typing aliases for integer-like #18096

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rgommers opened this issue Dec 30, 2020 · 5 comments · Fixed by #18185
Closed

typing aliases for integer-like #18096

rgommers opened this issue Dec 30, 2020 · 5 comments · Fixed by #18185

Comments

@rgommers
Copy link
Member

When trying to use numpy.typing-provided aliases for integer-like scalars in scipy/scipy#10844 (review), I noticed that numpy.typing doesn't have the right alias. It does have:

_IntLike = Union[_BoolLike, int, np.integer]

but accepting bools as 0/1 where integers are expected isn't good practice.

Accepting integers for float-like is fine, and mypy does accept int if you annotate a variable as float. Complex I'm not sure about, but I'd think it's undesirable to accept ints/floats for complex-like.

@BvB93
Copy link
Member

BvB93 commented Dec 30, 2020

The defition of the <X>Like aliases is currently based on whether or not the scalar-type can be cast into <X> (assuming casting="same_kind"), so the assumption here is more or less that the value will be fed to np.arrray() or an equivalent.

Obviously this means that _IntLike is not always a good fit for every situation, especially when indexing is in concerned with np.bool_.__index__ being deprecated.
In the latter situation I'd just stick to something akin to Union[int, np.integer], or if the situation allows for it: the typing.SupportsIndex protocol.

# The 6 `<X>Like` type-aliases below represent all scalars that can be
# coerced into `<X>` (with the casting rule `same_kind`)
_BoolLike = Union[bool, np.bool_]
_UIntLike = Union[_BoolLike, np.unsignedinteger]
_IntLike = Union[_BoolLike, int, np.integer]
_FloatLike = Union[_IntLike, float, np.floating]
_ComplexLike = Union[_FloatLike, complex, np.complexfloating]
_TD64Like = Union[_IntLike, np.timedelta64]

As a side note, mypy already considers complex a super-type of float (same as for float and int).
So if one is interested in accepting the former but rejecting the latter they would have to explicitly add a float-based overload returning NoReturn:

from typing import overload, NoReturn

@overload 
def func(a: complex) -> complex: ...
@overload 
def func(a: float) -> NoReturn: ...

@rgommers
Copy link
Member Author

The defition of the <X>Like aliases is currently based on whether or not the scalar-type can be cast into <X> (assuming casting="same_kind"), so the assumption here is more or less that the value will be fed to np.arrray() or an equivalent.

I can't think of many situations where I'd want to use this though. NumPy itself is fairly inconsistent, e.g.:

In [1]: x = np.array([True, False])

In [2]: np.sin(x)
Out[2]: array([0.8413, 0.    ], dtype=float16)

In [3]: np.add(x, x)  # hard to guess if this will upcast or not without trying
Out[3]: array([ True, False])

In [4]: np.add.reduce(x, x)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-8a94aae9244a> in <module>
----> 1 np.add.reduce(x, x)

TypeError: only integer scalar arrays can be converted to a scalar index

In [5]: np.sum(x)
Out[5]: 1

In [6]: np.add(x, [1, 2])
Out[6]: array([2, 2])

In [7]: np.subtract(x, x)  # This reasoning makes little sense, no one really means bitwise_xor
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-294a82b71bf3> in <module>
----> 1 np.subtract(x, x)

TypeError: numpy boolean subtract, the `-` operator, is not supported, use the bitwise_xor, the `^` operator, or the logical_xor function instead.

I'd like all of that to consistently error where it casts to integer now. Which is not something we can easily do - but if we're going to expose an alias in numpy.typing as public API, why not make it the thing people should be using for new code when working with integers?

@leofang
Copy link
Contributor

leofang commented Jan 3, 2021

@BvB93
Copy link
Member

BvB93 commented Jan 11, 2021

Which is not something we can easily do - but if we're going to expose an alias in numpy.typing as public API, why not make it the thing people should be using for new code when working with integers?

@rgommers If the plan is to expose them then I'd definitely agree with you.

The current aliases are IMO too useful to remove in their entirety, so instead I'd propose to just give them a more apropiate. Perhaps something along the lines of _IntLike_co and _FloatLike_co (the same holds for _ArrayLike<X>)?

@rgommers
Copy link
Member Author

_co sounds good to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants