Skip to content

Commit b17ae30

Browse files
Update type narrowing section incommon_issues.rst (#11014)
1. Changed an outdated example to an original one from python/typing#15 (comment) 2. I've listed more type narrowing techniques, like `type(obj) is some_class` 3. Fixed several headers Co-authored-by: Terence Honles <[email protected]>
1 parent 5c4aea3 commit b17ae30

File tree

1 file changed

+33
-23
lines changed

1 file changed

+33
-23
lines changed

docs/source/common_issues.rst

+33-23
Original file line numberDiff line numberDiff line change
@@ -367,19 +367,38 @@ above example:
367367
Complex type tests
368368
------------------
369369

370-
Mypy can usually infer the types correctly when using :py:func:`isinstance <isinstance>`
371-
type tests, but for other kinds of checks you may need to add an
370+
Mypy can usually infer the types correctly when using :py:func:`isinstance <isinstance>`,
371+
:py:func:`issubclass <issubclass>`,
372+
or ``type(obj) is some_class`` type tests,
373+
and even user-defined type guards,
374+
but for other kinds of checks you may need to add an
372375
explicit type cast:
373376

374377
.. code-block:: python
375378
376-
def f(o: object) -> None:
377-
if type(o) is int:
378-
o = cast(int, o)
379-
g(o + 1) # This would be an error without the cast
380-
...
381-
else:
382-
...
379+
from typing import Sequence, cast
380+
381+
def find_first_str(a: Sequence[object]) -> str:
382+
index = next((i for i, s in enumerate(a) if isinstance(s, str)), -1)
383+
if index < 0:
384+
raise ValueError('No str found')
385+
386+
found = a[index] # Has `object` type, despite the fact that we know it is `str`
387+
return cast(str, found) # So, we need an explicit cast to make mypy happy
388+
389+
Alternatively, you can use ``assert`` statement together with some
390+
of the supported type inference techniques:
391+
392+
.. code-block:: python
393+
394+
def find_first_str(a: Sequence[object]) -> str:
395+
index = next((i for i, s in enumerate(a) if isinstance(s, str)), -1)
396+
if index < 0:
397+
raise ValueError('No str found')
398+
399+
found = a[index] # Has `object` type, despite the fact that we know it is `str`
400+
assert isinstance(found, str) # Now, `found` will be narrowed to `str` subtype
401+
return found # No need for the explicit `cast()` anymore
383402
384403
.. note::
385404

@@ -390,19 +409,11 @@ explicit type cast:
390409
runtime. The cast above would have been unnecessary if the type of
391410
``o`` was ``Any``.
392411

393-
Mypy can't infer the type of ``o`` after the :py:class:`type() <type>` check
394-
because it only knows about :py:func:`isinstance` (and the latter is better
395-
style anyway). We can write the above code without a cast by using
396-
:py:func:`isinstance`:
412+
.. note::
397413

398-
.. code-block:: python
414+
You can read more about type narrowing techniques here.
399415

400-
def f(o: object) -> None:
401-
if isinstance(o, int): # Mypy understands isinstance checks
402-
g(o + 1) # Okay; type of o is inferred as int here
403-
...
404-
405-
Type inference in mypy is designed to work well in common cases, to be
416+
Type inference in Mypy is designed to work well in common cases, to be
406417
predictable and to let the type checker give useful error
407418
messages. More powerful type inference strategies often have complex
408419
and difficult-to-predict failure modes and could result in very
@@ -621,7 +632,7 @@ You can install the latest development version of mypy from source. Clone the
621632
sudo python3 -m pip install --upgrade .
622633
623634
Variables vs type aliases
624-
-----------------------------------
635+
-------------------------
625636

626637
Mypy has both type aliases and variables with types like ``Type[...]`` and it is important to know their difference.
627638

@@ -662,7 +673,7 @@ Mypy has both type aliases and variables with types like ``Type[...]`` and it is
662673
def fun2(x: tp) -> None: ... # error: Variable "__main__.tp" is not valid as a type
663674
664675
Incompatible overrides
665-
------------------------------
676+
----------------------
666677

667678
It's unsafe to override a method with a more specific argument type,
668679
as it violates the `Liskov substitution principle
@@ -773,7 +784,6 @@ False:
773784
If you use the :option:`--warn-unreachable <mypy --warn-unreachable>` flag, mypy will generate
774785
an error about each unreachable code block.
775786

776-
777787
Narrowing and inner functions
778788
-----------------------------
779789

0 commit comments

Comments
 (0)