From 14602194138018c2d98bcb96569aa73f6bb47fbe Mon Sep 17 00:00:00 2001 From: Greg Price Date: Sun, 5 Jun 2016 19:23:45 -0700 Subject: [PATCH 1/2] More improvements to cheat sheet I made a revision pass through the whole document, and made several kinds of changes: * Organize it all in hopes of making things easier to find with thematic groupings, headers, spacing, and putting the key words at the start of each description. * Give the descriptions more of a common style. * Explain a few things like `reveal_type` and `Sequence` a little more fully. * Cut the redundant `Tuple` example. * Add descriptions for the class-definition example. * Add a description for the generator-function example, and change its return-type annotation from `Iterator` to `Iterable`. That gets us out of trying to explain the difference between those, as `Iterable` is the one we mention elsewhere for function parameters. And as far as I can see, there isn't a reason we need to recommend `Iterator` instead here. I'd have liked also to switch from recommending `six.text_type` to `typing.Text`, but that should wait until 3.5.2, with `Text`, is out. --- docs/source/cheat_sheet.rst | 175 ++++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 69 deletions(-) diff --git a/docs/source/cheat_sheet.rst b/docs/source/cheat_sheet.rst index 0e88ed1f3eea..76beedcc2d1a 100644 --- a/docs/source/cheat_sheet.rst +++ b/docs/source/cheat_sheet.rst @@ -1,7 +1,7 @@ Mypy syntax cheat sheet (Python 2) ================================== -This document is a quick cheat sheet showing how the PEP-484 type +This document is a quick cheat sheet showing how the PEP 484 type language represents various common types in Python 2. .. note:: @@ -11,121 +11,158 @@ language represents various common types in Python 2. many of the examples have a dual purpose: show how to write the annotation, and show the inferred types. + +Built-in types +************** + .. code-block:: python - from typing import List, Dict, Set, Tuple, Union, Optional, Callable, Match - from six import text_type + from typing import List, Set, Dict, Tuple, Optional + import six - # For builtin types, just use the name of the type + # For simple built-in types, just use the name of the type. x = 1 # type: int x = 1.0 # type: float x = "test" # type: str x = u"test" # type: unicode - # Print an error message giving the type of x (it's also a runtime - # error, so don't leave it in) - reveal_type(x) - # For collections, the name of the type is capitalized, and the - # name of the type in the collection is in brackets. + # name of the type inside the collection is in brackets. x = [1] # type: List[int] x = set([6, 7]) # type: Set[int] - # For mappings, we need the types of both keys and values + + # For mappings, we need the types of both keys and values. x = dict(field=2.0) # type: Dict[str, float] - # For tuples, we specify the types of all the elements + + # For tuples, we specify the types of all the elements. x = (3, "yes", 7.5) # type: Tuple[int, str, float] - # six.text_type is str/unicode in Python 2 and string but not bytes in Python 3 - x = ["string", u"unicode"] # type: List[text_type] - # If something could be one of a few types, use Union - x = [3, 5, "test", "fun"] # type: List[Union[int, str]] - x = re.match(r'[0-9]+', "15") # type: Match[str] + # For textual data, we generally use six.text_type. + # This is `unicode` in Python 2 and `str` in Python 3. + x = ["string", u"unicode"] # type: List[six.text_type] - # If you don't know the type of something, you can use Any - x = mystery_function() # type: Any - # And if you want to have something not be type-checked, you can - # use ignore to suppress mypy warnings for a given line - # Ideally, one would never use this - x = confusing_function() # type: ignore + # Use Optional for values that could be None. + input_str = f() # type: Optional[str] + if input_str is not None: + print input_str - # This is how you annotate a function definition + +Functions +********* + +.. code-block:: python + + from typing import Callable, Iterable + + # This is how you annotate a function definition. def stringify(num): # type: (int) -> str """Your function docstring goes here after the type definition.""" return str(num) - # And here's how you specify multiple arguments + # And here's how you specify multiple arguments. def plus(num1, num2): # type: (int, int) -> int return num1 + num2 - # Add type annotations for kwargs as though they were positional args + # Add type annotations for kwargs as though they were positional args. def f(num1, my_float=3.5): # type: (int, float) -> float return num1 + my_float - # This is how you annotate a function value + + # This is how you annotate a function value. x = f # type: Callable[[int, float], float] - # Use Optional[Type] for objects that could be None - def f(input_str=None): - # type: (Optional[str]) -> int - if input_str is not None: - return len(input_str) - return 0 - - from typing import Mapping, MutableMapping - # Dict is a python dictionary - # MutableMapping is an abstract base class for a dict-type thing - # Mapping is an abtract base class for a dict-type thing that may - # not support writing to the mapping + # A generator function that yields ints is secretly just a function that + # returns an iterable (see below) of ints, so that's how we annotate it. + def f(n): + # type: (int) -> Iterable[int] + i = 0 + while i < n: + yield i + i += 1 + + +What to do when puzzled +*********************** + +.. code-block:: python + + from typing import Union, Any + + # To find out what type mypy infers for an expression anywhere in + # your program, wrap it in reveal_type. Mypy will print an error + # message with the type; remove it again before running the code. + reveal_type(1) # -> error: Revealed type is 'builtins.int' + + # Use Union when something could be one of a few types. + x = [3, 5, "test", "fun"] # type: List[Union[int, str]] + + # Use Any if you don't know the type of something. + x = mystery_function() # type: Any + + # Use `ignore` if you want to have something not be type-checked, + # to suppress mypy warnings for a given line. + # Ideally, one would never use this. + x = confusing_function() # type: ignore + + +Standard duck types / ABCs +************************** + +.. code-block:: python + + from typing import Mapping, MutableMapping, Sequence, Iterator + + # Use Iterable for generic iterables (anything usable in `for`), + # and Sequence where a sequence (supporting `len` and `__getitem__`) is required. + def f(iterable_of_ints): + # type: (Iterable[int]) -> List[str] + return [str(x) for x in iterator_of_ints] + f(range(1, 3)) + + # Mapping describes a dict-like object (with `__getitem__`) that we won't mutate, + # and MutableMapping one (with `__setitem__`) that we might. def f(my_dict): # type: (Mapping[int, str]) -> List[int] return list(my_dict.keys()) f({3: 'yes', 4: 'no'}) - def f(my_mapping): # type: (MutableMapping[int, str]) -> Set[str] my_dict[5] = 'maybe' return set(my_dict.values()) f({3: 'yes', 4: 'no'}) - from typing import Sequence, Iterable, Generator - # Use Iterable[Type] for generic iterators - # Sequence[Type] is abstract base class for list-like iterables - def f(iterator_of_ints): - # type: (Sequence[int]) -> List[str] - return [str(x) for x in iterator_of_ints] - f(range(1, 3)) - - from typing import Tuple - def f(my_tuple): - # type: (Tuple[int, int]) -> int - return sum([val for val in my_tuple]) - f((1, 2)) - from typing import Iterator - def f(n): - # type: (int) -> Iterator[int] - i = 0 - while i < n: - yield i - i += 1 - f(5) +Classes +******* - # TODO: Add typevar example +.. code-block:: python - # This is how you annotate a class with '__init__' constructor and a method. class MyClass(object): - """This is where your class docstring goes.""" - - def __init__(self): - # type: () -> None - """Add your constructor stuff here.""" - pass + # For instance methods, omit `self`. def my_class_method(self, num, str1): # type: (int, str) -> str - """Returns 'str1' repeated 'num' times.""" return num * str1 + # The __init__ method doesn't return anything, so it gets return + # type None just like any other method that doesn't return anything. + def __init__(self): + # type: () -> None + pass + + # User-defined classes are written with just their own names. x = MyClass() # type: MyClass + + +Other stuff +*********** + +.. code-block:: python + + # typing.Match describes regex matches from the re module. + from typing import Match + x = re.match(r'[0-9]+', "15") # type: Match[str] + + # TODO: Add typevar example From c510988fdbff241128c0024eb8defb93e7ec2a74 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 6 Jun 2016 12:30:46 -0700 Subject: [PATCH 2/2] Act on review feedback --- docs/source/cheat_sheet.rst | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/source/cheat_sheet.rst b/docs/source/cheat_sheet.rst index 76beedcc2d1a..1a041a235ce2 100644 --- a/docs/source/cheat_sheet.rst +++ b/docs/source/cheat_sheet.rst @@ -83,8 +83,8 @@ Functions i += 1 -What to do when puzzled -*********************** +When you're puzzled or when things are complicated +************************************************** .. code-block:: python @@ -98,17 +98,30 @@ What to do when puzzled # Use Union when something could be one of a few types. x = [3, 5, "test", "fun"] # type: List[Union[int, str]] - # Use Any if you don't know the type of something. + # Use Any if you don't know the type of something or it's too + # dynamic to write a type for. x = mystery_function() # type: Any - # Use `ignore` if you want to have something not be type-checked, - # to suppress mypy warnings for a given line. - # Ideally, one would never use this. - x = confusing_function() # type: ignore + # Use `ignore` to suppress type-checking on a given line, when your + # code confuses mypy or runs into an outright bug in mypy. + # Good practice is to comment every `ignore` with a bug link + # (in mypy, typeshed, or your own code) or an explanation of the issue. + x = confusing_function() # type: ignore # https://github.com/python/mypy/issues/1167 + # TODO: explain cast -Standard duck types / ABCs -************************** + # TODO: explain "Need type annotation for variable" when + # initializing with None or an empty container + + +Standard duck types +******************* + +In typical Python code, many functions that can take a list or a dict +as an argument only need their argument to be somehow "list-like" or +"dict-like". A specific meaning of "list-like" or "dict-like" (or +something-else-like) is called a "duck type", and several duck types +that are common in idiomatic Python are standardized. .. code-block:: python @@ -165,4 +178,8 @@ Other stuff from typing import Match x = re.match(r'[0-9]+', "15") # type: Match[str] - # TODO: Add typevar example + # TODO: add typing.IO: e.g., sys.stdout has type IO[str] + + # TODO: add TypeVar and a simple generic function + + # TODO: add AnyStr (and mention up next to strings)