mypy is a static type checker for Python. By adding type annotations to your code mypy can find a variety of bugs. These type annotations also act as machine-checked documentation of your code, and your IDE can make use of them to improve its code completion. They doesn't affect how your program runs, as the Python interpreter ignores these type annotations at run-time
Press Spacebar
to go to the next slide (or ?
to see all navigation shortcuts)
Lunch Time Python, Scientific Software Center, Heidelberg University
Imagine you call a function with the wrong type of object
str
+ int
These slides also use the nb-mypy extension:
python -m pip install nb-mypy
This automatically runs mypy on every cell before it is executed.
This is just for convenience to show the mypy output for this talk.
In general I would recommend running mypy separately or as a pre-commit hook.
if "google.colab" in str(get_ipython()):
!pip install nb-mypy -qqq
%load_ext nb_mypy
Version 1.0.5
def greet(thing):
return f"Hello {thing}"
print(greet("world"))
Hello world
print(greet(True))
Hello True
print(greet(greet))
Hello <function greet at 0x7fa421f03a30>
def greet(thing: str) -> str:
return f"Hello {thing}"
print(greet("world"))
Hello world
print(greet(True))
<cell>1: error: Argument 1 to "greet" has incompatible type "bool"; expected "str" [arg-type]
Hello True
print(greet(greet))
<cell>1: error: Argument 1 to "greet" has incompatible type "Callable[[str], str]"; expected "str" [arg-type]
Hello <function greet at 0x7fa4103277f0>
:
my_string: str
->
def hello() -> str:
None
, int
, float
, str
, bool
can be used directlylist
, dict
, tuple
, set
from typing import List
list
directly (only with Python >= 3.9)def mul(a, b):
return a * b
print(mul(2, 2))
4
print(mul(2, [0]))
[0, 0]
print(mul(2, "really?"))
really?really?
def mul(a: float, b: float) -> float:
return a * b
print(mul(2, 2))
4
print(mul(2, [0]))
<cell>1: error: Argument 2 to "mul" has incompatible type "List[int]"; expected "float" [arg-type]
[0, 0]
print(mul(2, "really?"))
<cell>1: error: Argument 2 to "mul" has incompatible type "str"; expected "float" [arg-type]
really?really?
# does operation & returns result
def append1(l):
return l + [1]
# does operation in place
def append2(l):
l.append(1)
l0 = [0]
print(append1(l0))
[0, 1]
print(append2(l0))
None
from typing import List
def append1(l: List[int]) -> List[int]:
return l + [1]
def append2(l: List[int]) -> None:
l.append(1)
l0 = [0]
print(append1(l0))
[0, 1]
print(append2(l0))
<cell>1: error: "append2" does not return a value [func-returns-value]
None
def count(l: List[str]):
return len(l)
a = []
# a: List[str] = []
# a.append("ok")
# a = ["ok"]
print(count(a))
<cell>5: error: Need type annotation for "a" (hint: "a: List[<type>] = ...") [var-annotated]
0
from typing import List, Tuple
def count(coords: List[Tuple[float, float]]) -> int:
return len(coords)
print(count([(0, 0), (1, 1), (2, 2)]))
3
from typing import Dict
def invert(ages: Dict[str, int]) -> Dict[int, str]:
return {key: value for value, key in ages.items()}
ages = {"bob": 2, "joe": 7}
names = invert(ages)
print(names)
{2: 'bob', 7: 'joe'}
my_obj: Iterable
my_obj: Sequence
my_obj: Mapping
my_obj: MutableMapping
from typing import Iterable
def count(items: Iterable):
i = 0
for item in items:
i += 1
return i
print(count(["a", "b", "c"]))
3
from typing import Sequence
def last(items: Sequence):
return items[-1]
print(last([1, 2, 3]))
3
print(count(["a", "b", "c"]))
from typing import Dict, Mapping
def invert(ages: Mapping[str, int]) -> Dict[int, str]:
# ages["simon"] = 12
return {key: value for value, key in ages.items()}
ages = {"bob": 2, "joe": 7}
names = invert(ages)
print(names)
3 {2: 'bob', 7: 'joe'}
my_obj: Union[str, float]
my_obj: Union[Dict, None]
my_obj: Optional[Dict]
my_obj: Any
def hi(name=None):
if name is None:
name = "you"
print(f"hello {name}")
hi()
hi("joe")
hello you hello joe
from typing import Union
def hi(name: Union[str, None] = None) -> None:
if name is None:
name = "you"
print(f"hello {name}")
hi()
hi("joe")
hello you hello joe
from typing import Optional
def hi(name: Optional[str] = None) -> None:
if name is None:
name = "you"
print(f"hello {name}")
hi()
hi("joe")
hello you hello joe
template<typename T>
# concatenation of lists of a single type
def add(x, y):
return x + y
# desired use
print(add([1, 2], [3]))
print(add(["A"], ["B"]))
[1, 2, 3] ['A', 'B']
# don't want to allow e.g.
print(add([1.0], ["B"])) # this shouldn't be allowed
print(add([[0]], [{1, 2}])) # this shouldn't be allowed
[1.0, 'B'] [[0], {1, 2}]
from typing import TypeVar, List
T = TypeVar("T")
def add(x: List[T], y: List[T]) -> List[T]:
return x + y
print(add([1, 2], [3]))
print(add(["A"], ["B"]))
[1, 2, 3] ['A', 'B']
add([1.0], ["B"]) # this shouldn't be allowed
add([[0]], [{1, 2}]) # this shouldn't be allowed
<cell>1: error: Cannot infer type argument 1 of "add" [misc] <cell>2: error: Cannot infer type argument 1 of "add" [misc]
[[0], {1, 2}]
from typing import TypeVar
IntOrStr = TypeVar("IntOrStr", str, int)
def add(x: List[IntOrStr], y: List[IntOrStr]) -> List[IntOrStr]:
return x + y
print(add([1, 2], [3]))
print(add(["A"], ["B"]))
[1, 2, 3] ['A', 'B']
print(add([1.0, 2.0], [3.0]))
<cell>1: error: Value of type variable "IntOrStr" of "add" cannot be "float" [type-var]
[1.0, 2.0, 3.0]
# type: ignore
reveal_type(my_obj)
cast(str, my_obj)
def add1(x: int) -> int:
return x + 1
f = 0.1
reveal_type(f)
<cell>7: note: Revealed type is "builtins.float"
# (mis)use of type ignore:
print(add1(f)) # type: ignore
1.1
from typing import cast
# (mis)use of cast:
print(add1(cast(int, f)))
1.1
pip install types-requests
--install-types
to automatically download them as neededIf no type information is available, you can add # type: ignore
to the end of the line where you import the package to suppress mypy error messages related to this package.
Iterable[T]
list[X]
Add type annotations and run mypy to catch bugs earlier and more easily
a = [1, 2, 3]
is automatically inferred to be a List[int]