-
-
Notifications
You must be signed in to change notification settings - Fork 3k
More improvements to cheat sheet #1659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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,175 @@ 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How should we introduce AnyStr? It's important but would be confusing this early. |
||
# This is `unicode` in Python 2 and `str` in Python 3. | ||
x = ["string", u"unicode"] # type: List[six.text_type] | ||
|
||
# Use Optional for values that could be None. | ||
input_str = f() # type: Optional[str] | ||
if input_str is not None: | ||
print input_str | ||
|
||
# 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 | ||
|
||
# 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically it's an Iterator. Not sure if this distinction matters, given that Iterator inherits from Iterable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. I wrote Iterator first for that reason, then contemplated explaining the distinction between Iterator and Iterable (having already recommended Iterable elsewhere), and concluded there isn't a good explanation in this context because the distinction doesn't actually matter. |
||
def f(n): | ||
# type: (int) -> Iterable[int] | ||
i = 0 | ||
while i < n: | ||
yield i | ||
i += 1 | ||
|
||
|
||
When you're puzzled or when things are complicated | ||
************************************************** | ||
|
||
.. 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 or it's too | ||
# dynamic to write a type for. | ||
x = mystery_function() # type: Any | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this probably needs more explanation of the tradeoffs around using Any. E.g. for Zulip, we've been encouraging folks to only use Any in rare circumstances. One thing I have noticed is
|
||
|
||
# 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 | ||
|
||
# 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 | ||
|
||
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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also add typing.IO for streams. E.g. sys.stdout has type IO[str]. |
||
|
||
# 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make PEP 484 a link