Optional types and the None type
You can use the Optional
type modifier to define a type variantthat allows None
, such as Optional[int]
(Optional[X]
isthe preferred shorthand for Union[X, None]
):
- from typing import Optional
- def strlen(s: str) -> Optional[int]:
- if not s:
- return None # OK
- return len(s)
- def strlen_invalid(s: str) -> int:
- if not s:
- return None # Error: None not compatible with int
- return len(s)
Most operations will not be allowed on unguarded None
or Optional
values:
- def my_inc(x: Optional[int]) -> int:
- return x + 1 # Error: Cannot add None and int
Instead, an explicit None
check is required. Mypy haspowerful type inference that lets you use regular Pythonidioms to guard against None
values. For example, mypyrecognizes is None
checks:
- def my_inc(x: Optional[int]) -> int:
- if x is None:
- return 0
- else:
- # The inferred type of x is just int here.
- return x + 1
Mypy will infer the type of x
to be int
in the else block due to thecheck against None
in the if condition.
Other supported checks for guarding against a None
value includeif x is not None
, if x
and if not x
. Additionally, mypy understandsNone
checks within logical expressions:
- def concat(x: Optional[str], y: Optional[str]) -> Optional[str]:
- if x is not None and y is not None:
- # Both x and y are not None here
- return x + y
- else:
- return None
Sometimes mypy doesn’t realize that a value is never None
. This notablyhappens when a class instance can exist in a partially defined state,where some attribute is initialized to None
during objectconstruction, but a method assumes that the attribute is no longer None
. Mypywill complain about the possible None
value. You can useassert x is not None
to work around this in the method:
- class Resource:
- path: Optional[str] = None
- def initialize(self, path: str) -> None:
- self.path = path
- def read(self) -> str:
- # We require that the object has been initialized.
- assert self.path is not None
- with open(self.path) as f: # OK
- return f.read()
- r = Resource()
- r.initialize('/foo/bar')
- r.read()
When initializing a variable as None
, None
is usually anempty place-holder value, and the actual value has a different type.This is why you need to annotate an attribute in a cases like the classResource
above:
- class Resource:
- path: Optional[str] = None
- ...
This also works for attributes defined within methods:
- class Counter:
- def __init__(self) -> None:
- self.count: Optional[int] = None
As a special case, you can use a non-optional type when initializing anattribute to None
inside a class body and using a type comment,since when using a type comment, an initializer is syntactically required,and None
is used as a dummy, placeholder initializer:
- from typing import List
- class Container:
- items = None # type: List[str] # OK (only with type comment)
This is not a problem when using variable annotations, since no initializeris needed:
- from typing import List
- class Container:
- items: List[str] # No initializer
Mypy generally uses the first assignment to a variable toinfer the type of the variable. However, if you assign both a None
value and a non-None
value in the same scope, mypy can usually dothe right thing without an annotation:
- def f(i: int) -> None:
- n = None # Inferred type Optional[int] because of the assignment below
- if i > 0:
- n = i
- ...
Sometimes you may get the error “Cannot determine type of <something>”. In thiscase you should add an explicit Optional[…]
annotation (or type comment).
Note
None
is a type with only one value, None
. None
is also usedas the return type for functions that don’t return a value, i.e. functionsthat implicitly return None
.
Note
The Python interpreter internally uses the name NoneType
forthe type of None
, but None
is always used in typeannotations. The latter is shorter and reads better. (Besides,NoneType
is not even defined in the standard library.)
Note
Optional[…]
does not mean a function argument with a default value.However, if the default value of an argument is None
, you can usean optional type for the argument, but it’s not enforced by default.You can use the —no-implicit-optional
command-line option to stoptreating arguments with a None
default value as having an implicitOptional[…]
type. It’s possible that this will become the defaultbehavior in the future.