The builtins.reversed.__new__ signature is currently annotated like (Reversible[T] | SupportsLenAndGetItem[T]) -> Iterator[T]:
|
class reversed(Iterator[_T]): |
|
@overload |
|
def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] |
|
@overload |
|
def __new__(cls, sequence: SupportsLenAndGetItem[_T], /) -> Iterator[_T]: ... # type: ignore[misc] |
|
def __iter__(self) -> Self: ... |
|
def __next__(self) -> _T: ... |
|
def __length_hint__(self) -> int: ... |
It has two problems:
argument of type SupportsLenAndGetItem[T]
This should return a reversed[T] instance (i.e. Self), instead of "just" an Iterable[T], which is unnecessarily loose.
argument with type that implement __reversed__
This is only handled if the argument's __reversed__ returns an Iterator[T], i.e. arguments that are Reversible[T]. But in reality, reversed(x) is equivalent to x.__reversed__() (if __reversed__ exists). To illustrate:
>>> class Revver[X]:
... x: X
... def __init__(self, x: X):
... self.x = x
... def __reversed__(self) -> X:
... return self.x
...
>>> reversed(Revver(42))
42
However, this case isn't handled because Revver[X] cannot be expressed as a Reversible[?].
So instead, it should look something like this:
class SupportsReversed[T]:
def __reversed__(self) -> T: ...
class reversed[T](Iterator[T]):
@overload
def __new__[R](cls, sequence: SupportsReversed[R], /) -> R: ...
@overload
def __new__(cls, sequence: SupportsLenAndGetItem[T], /) -> Self: ...
def __length_hint__(self) -> int: ...
# btw, this is why protocols shouldn't be mixed with abstract classes:
def __iter__(self) -> Self: ...
def __next__(self) -> T: ...
Let me know if I should make this into PR :)
The
builtins.reversed.__new__signature is currently annotated like(Reversible[T] | SupportsLenAndGetItem[T]) -> Iterator[T]:typeshed/stdlib/builtins.pyi
Lines 1639 to 1646 in 3d26992
It has two problems:
argument of type
SupportsLenAndGetItem[T]This should return a
reversed[T]instance (i.e.Self), instead of "just" anIterable[T], which is unnecessarily loose.argument with type that implement
__reversed__This is only handled if the argument's
__reversed__returns anIterator[T], i.e. arguments that areReversible[T]. But in reality,reversed(x)is equivalent tox.__reversed__()(if__reversed__exists). To illustrate:However, this case isn't handled because
Revver[X]cannot be expressed as aReversible[?].So instead, it should look something like this:
Let me know if I should make this into PR :)