Optional values often have to tell the type checker that they are no longer optional. To do this, use assert
:
assert x is not None, "x should exist"
Other assertions will work as well, such as isinstance
for a Union
type:
def func(z: Union[int, str]) -> ...:
assert isinstance(z, int), "z should be an integer"
For more complex expressions, the assert_non_none
function may be useful:
from typing import TypeVar
T = TypeVar('T')
def assert_non_none(x: T, message) -> T:
if message:
assert x is not None, message
else:
assert x is not None
return x
(assert_non_none(x, "x should not be None") for x in some_iterable_of_optional_values)
{
assert_non_none(x.a, "x.a should not be None"): assert_non_none(x.b, "x.b should not be None")
for x in qs
}
If you are passing a class (and not an instance) to a function, the function is accepting a Type[...]
argument:
def func(cls: Type[SomethingThatCanBeInstantiated]) -> ...: ...
These cannot be fully typed. You should use Any
:
def func(*args: Any, **kwargs: Any) -> ...: ...
The cast()
function does not actually cast a value at runtime. It is only for type checkers. At runtime it only returns the second argument passed in.
You are encouraged to use generic types rather than very specific types, especially immutable versions. For example, the following is preferred:
def func(it: Sequence[T]) -> ...:
...
instead of:
def func(it: List[T]) -> ...:
Note how the above code uses a generic type T
.
The code in the first example guarantees it
will not be modified (it
is immutable).
This only applies to arguments, not return values. Return values can be these, unless the return value is expected to be mutable. You should still prefer immutable return types.
Use the following to help:
- Prefer
Mapping[]
overDict[]
- Prefer
Sequence[]
overList[]
- If you never use
len()
or index the argument, useIterable[]
- If you never use
Generator functions (those that use yield
) return Iterator
type. The following would be incorrect:
def func() -> Iterable[int]:
for x in range(1, 3):
yield x
This passes, but it is better to be more explicit with Iterator
.
The function returns a Generator
instance, of which Iterator
is a subclass.
def func() -> Sequence[int]: # Can also use Iterable[int]
collection = []
for x in range(1, 3):
collection.append(x)
return collection
Sequence
guarantees ability to index, but Iterable
does not. The more generic type would be Iterable
and this is okay if the function is returning a type that is not guaranteed to be indexable.
Iterable
also does not conform to the Sized
protocol. If you must call len()
on a sequence, you should specify Sequence
or Sized
as the type argument.
def func(c: Sized) -> int:
return len(c)
Collection
should be used when you have an iterable that is not a sequence but it is also sized.
def func(c: Collection) -> int:
len(c) # ok
for x in c: # ok
...
iter(c) # ok
x[0] # error
This section also applies to function arguments.
If you encounter a missing import error with Mypy, and your use of that library is very simple (such as only a few function calls), you are encouraged to create a stub in app/.mypy_stubs
.
If the use of the library is too complex (or if the library is too complex), you can add the imports to mypy.ini
like so:
[mypy-django_filters.rest_framework]
ignore_missing_imports = True
Note that the import must be complete. Wildcards will not work.
You should look for an existing library installable with Pip before creating stubs or setting ignore_missing_imports
.