Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd3bb2d2

Browse files
committed
Add additional examples about TypeIs
1 parent75caed7 commitd3bb2d2

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

‎docs/spec/narrowing.rst

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,78 @@ This code fails at runtime, because the narrower returns ``False`` (1 is not a `
227227
and the ``else`` branch is taken in ``takes_narrower()``.
228228
If the call ``takes_narrower(1, is_bool)`` was allowed, type checkers would fail to
229229
detect this error.
230+
231+
In some cases, it may not be possible to narrow a type fully from information
232+
available to the TypeIs function. In such cases, raising an error is the only
233+
possible option, as you have neither enough information to confirm or deny a
234+
type narrowing operation. This is most likely to occur with narrowing of generics.
235+
236+
To see why, we can look at the following example::
237+
238+
from typing_extensions import TypeVar, TypeIs
239+
from typing import Generic
240+
241+
X = TypeVar("X", str, int, str | int, covariant=True, default=str | int)
242+
243+
class A(Generic[X]):
244+
def __init__(self, i: X, /):
245+
self._i: X = i
246+
247+
@property
248+
def i(self) -> X:
249+
return self._i
250+
251+
252+
class B(A[X], Generic[X]):
253+
def __init__(self, i: X, j: X, /):
254+
super().__init__(i)
255+
self._j: X = j
256+
257+
@property
258+
def j(self) -> X:
259+
return self._j
260+
261+
def possible_problem(x: A) -> TypeIs[A[int]]:
262+
return isinstance(x.i, int)
263+
264+
def possible_correction(x: A) -> TypeIs[A[int]]:
265+
if type(x) is A:
266+
# only narrow cases we know about
267+
return isinstance(x.i, int)
268+
raise TypeError(
269+
f"Refusing to narrow Genenric type {type(x)!r}"
270+
f"from function that only knows about {A!r}"
271+
)
272+
273+
Because it is possible to attempt to narrow B,
274+
but A does not have appropriate information about B
275+
(or any other unknown subclass of A!) it's not possible to safely narrow
276+
in either direction. The general rule for generics is that if you do not know
277+
all the places a generic class is generic and do not enough of them to be
278+
absolutely certain, you cannot return True, and if you do not have a definitive
279+
counter example to the type to be narrowed to you cannot return False.
280+
In practice, if soundness is prioritized over an unsafe narrowing,
281+
not knowing what you don't know is solvable by erroring out
282+
or by making the class to be narrowed final to avoid such a situation.
283+
284+
In practice, such correctness is not always neccessary, and may work against
285+
your needs. for example, if you trust that users implementing
286+
the Sequence Protocol are doing so in a way that is safe to iterate over,
287+
the following function can never be fully sound, but fully soundness is not necessarily
288+
easier or better for your use::
289+
290+
def useful_unsoundness(s: Sequence[object]) -> TypeIs[Sequence[int]]:
291+
return all(isinstance(i, int) for i in s)
292+
293+
However, many cases of this sort can be extracted for safe use with an alternative construction
294+
if soundness is of a high priority, and the cost of a copy is acceptable::
295+
296+
def safer(s: Sequence[object]) -> Sequence[int]:
297+
ret = tuple(i for i in s if isinstance(i, int))
298+
if len(ret) != len(s):
299+
raise TypeError
300+
return ret
301+
302+
Ultimately, TypeIs allows a very large amount of flexibility in handling type-narrowing,
303+
at the cost of more of the issues of evaluating when it is use is safe being left
304+
in the hands of developers.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp