From 10ae9120fe2986dd3ddecc047c692327e2e82e67 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 29 Mar 2023 22:38:57 -0700 Subject: [PATCH] Improvements to cheat sheet (#14972) Everyone can use future annotations now. Also forward references are the most type hinting popular question on Stack Overflow, so give a little more detail. The IO example should go with standard duck types. I moved the async example to the bottom. It basically just works as you'd expect, maybe we should remove it altogether. Linking #13681 --------- Co-authored-by: Ethan Smith --- docs/source/cheat_sheet_py3.rst | 89 ++++++++++++++++++-------------- docs/source/runtime_troubles.rst | 2 + 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index 5aa1770512b8..24621537cf62 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -21,7 +21,7 @@ See :ref:`type-inference-and-annotations` for more details. # You don't need to initialize a variable to annotate it a: int # Ok (no value at runtime until assigned) - # Doing so is useful in conditional branches + # Doing so can be useful in conditional branches child: bool if age < 18: child = True @@ -34,7 +34,7 @@ Useful built-in types .. code-block:: python - # For most types, just use the name of the type. + # For most types, just use the name of the type in the annotation # Note that mypy can usually infer the type of a variable from its value, # so technically these annotations are redundant x: int = 1 @@ -75,10 +75,11 @@ Useful built-in types # Use Optional[X] for a value that could be None # Optional[X] is the same as X | None or Union[X, None] x: Optional[str] = "something" if some_condition() else None - # Mypy understands a value can't be None in an if-statement if x is not None: + # Mypy understands x won't be None here because of the if-statement print(x.upper()) - # If a value can never be None due to some invariants, use an assert + # If you know a value can never be None due to some logic that mypy doesn't + # understand, use an assert assert x is not None print(x.upper()) @@ -259,6 +260,8 @@ When you're puzzled or when things are complicated In some cases type annotations can cause issues at runtime, see :ref:`runtime_troubles` for dealing with this. +See :ref:`silencing-type-errors` for details on how to silence errors. + Standard "duck types" ********************* @@ -294,37 +297,11 @@ that are common in idiomatic Python are standardized. f({3: 'yes', 4: 'no'}) - -You can even make your own duck types using :ref:`protocol-types`. - -Coroutines and asyncio -********************** - -See :ref:`async-and-await` for the full detail on typing coroutines and asynchronous code. - -.. code-block:: python - - import asyncio - - # A coroutine is typed like a normal function - async def countdown35(tag: str, count: int) -> str: - while count > 0: - print(f'T-minus {count} ({tag})') - await asyncio.sleep(0.1) - count -= 1 - return "Blastoff!" - - -Miscellaneous -************* - -.. code-block:: python - import sys from typing import IO - # Use IO[] for functions that should accept or return any - # object that comes from an open() call (IO[] does not + # Use IO[str] or IO[bytes] for functions that should accept or return + # objects that come from an open() call (note that IO does not # distinguish between reading, writing or other modes) def get_sys_IO(mode: str = 'w') -> IO[str]: if mode == 'w': @@ -334,19 +311,38 @@ Miscellaneous else: return sys.stdout - # Forward references are useful if you want to reference a class before - # it is defined + +You can even make your own duck types using :ref:`protocol-types`. + +Forward references +****************** + +.. code-block:: python + + # You may want to reference a class before it is defined. + # This is known as a "forward reference". def f(foo: A) -> int: # This will fail at runtime with 'A' is not defined ... - class A: + # However, if you add the following special import: + from __future__ import annotations + # It will work at runtime and type checking will succeed as long as there + # is a class of that name later on in the file + def f(foo: A) -> int: # Ok ... - # If you use the string literal 'A', it will pass as long as there is a - # class of that name later on in the file - def f(foo: 'A') -> int: # Ok + # Another option is to just put the type in quotes + def f(foo: 'A') -> int: # Also ok ... + class A: + # This can also come up if you need to reference a class in a type + # annotation inside the definition of that class + @classmethod + def create(cls) -> A: + ... + +See :ref:`forward-references` for more details. Decorators ********** @@ -365,3 +361,20 @@ Decorator functions can be expressed via generics. See def decorator_args(url: str) -> Callable[[F], F]: ... + +Coroutines and asyncio +********************** + +See :ref:`async-and-await` for the full detail on typing coroutines and asynchronous code. + +.. code-block:: python + + import asyncio + + # A coroutine is typed like a normal function + async def countdown(tag: str, count: int) -> str: + while count > 0: + print(f'T-minus {count} ({tag})') + await asyncio.sleep(0.1) + count -= 1 + return "Blastoff!" diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index a62652111de6..0847a5796512 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -117,6 +117,8 @@ Since code inside ``if TYPE_CHECKING:`` is not executed at runtime, it provides a convenient way to tell mypy something without the code being evaluated at runtime. This is most useful for resolving :ref:`import cycles `. +.. _forward-references: + Class name forward references -----------------------------