Summary
Add a new TypeExclude to typing for static subtractive types.
Motive
Python typing is good at “addition”, such as typing.Union and type uinon in 3.10), but “subtraction” ability is still incomplete.
Concepts
Grammar & Usage
typing.TypeExclude[T, T1, T2, ...] means “instances of T, except of instances of T1 and T2 and …”.
Compare to type.Union[T1, T2, T3, ...] standing for T1 or T2 or T3 or ..., typing.TypeExclude[T, T1, T2, ...] stands for T - T1 -T2 - ...
For example:
from typing import TypeExclude
class Foo: ...
class Bar1(Foo): ...
class Bar2(Foo): ...
class Bar3(Foo): ...
def baz(t: TypeExclude[Foo, Bar3]) -> None: ...
baz(Foo()) # passed
baz(Bar1()) # passed
baz(Bar2()) # passed
baz(Bar3()) # Type checker error! `Bar3` is excluded from `Foo`
Advantages
- terse expression:
class Foo: ...
class Bar1(Foo): ...
class Bar2(Foo): ...
class Bar3(Foo): ...
class Bar4(Foo): ...
class Bar5(Foo): ...
# you need to write like this before to exclude `Bar5`
def baz(t: Bar1 | Bar2 | Bar3 | Bar4) -> None: ...
# now you can write like this
from typing import TypeExclude
def baz(t: TypeExclude[Foo, Bar5]) -> None: ...
- clear semantic: Annotate “excluding types” directly.
Example
Think about we have a base class for all api exceptions ApiException, and several of subclasses of it, such as ApiNotFoundError, EmptyApiError, ApiServerError, etc.
We are now creating a function to handle all api exceptions except for ApiServerError because users cannot solve it.
To select handlable exceptions, we have to write the type hint like this:
exception: ApiNotFoundError | EmptyApiError | <other ApiException except of ApiServerError>
This is too verbose – we only want to express “api exceptions except of ApiServerError”, but we had to write “what is avaliable” instead of “what is not allowed”. With typing.TypeExclude, we can write the type hint like this:
exception: typing.TypeExclude[ApiException, ApiServerError]
It means that "All ApiException instance is allowed, except for instances of ApiServerError.
This is clear and terse, and the more subclasses you have, the more concise TypeExclude is.
Potential Problems
- Difficult implementation for type exclusion?
- Static or runtime?
- Execution overheads?
Anyone has the same feeling? Or any enchancing idea?