Question 1: What is the inferred type of a function argument with a default value but no type annotation?
In 2017, Guido van Rossum, the creator of the Python language, suggested changing PEP 484 to specify that function arguments would infer their types from default values.
However, as of October 2020, PEP 484 states in section "The Any type:
A function parameter without an annotation is assumed to be annotated with Any.
In the discussion points below from mypy issue #3090 (Infer argument type from default value), Jukka reinforces the fact that default values do not change the default behavior of inferring unannotated parameters as having type Any.
Jukka Lehtosalo wrote:
Mypy follows PEP 484. A function parameter without an annotation is the same as having an Any annotation, and there is no exception for default values. Here is some rationale for this:
- Often the default argument is not enough to infer a type. For example,
None doesn't give enough context. With the current rules this doesn't pose a difficulty.
- The default argument may not be enough to infer the correct type. For example, if the default is
'' in Python 2, the correct type could well be Union[str, unicode]. If the default is 0, the correct type might well be float. If the programmer understands the rules for type inference they could override the default type with an annotation as needed, but this would add some extra complexity.
- The default value could be a complex expression such as a function call, and thus the type may not [be] obvious from just the default value. Having an annotation makes code like this easier to read.
- If a function has no annotation at all, then the argument type would have to be
Any anyway. The current rule makes this consistent across functions with no annotations and functions with partial annotations.
It all boils down to the current rule being simple and obvious, and in practice it doesn't feel much of a burden to add a few extra : int annotations. There is no deep technical reason for the current rule, though it makes type inference easier for mypy.
Then Guido responded:
OTOH most of those also apply to regular assignments, and there the rule is that
x = 0
infers type int for x. In practice this is very common and useful and only occasionally needs help. So I think we might use the same rule for default values and the complexity of explaining things wouldn't really change. I'm willing to make this a PEP 484 change if enough people care.
Question 2: What is the inferred type of a variable initialized as None?
When a variable is initialized as None, it is directly inferred that the variable is allowed to be explicitly assigned to None. If the variable is then later assigned a value, such as:
bazz = None
bazz = 42 # type: Optional[int]
then the type is inferred to be Optional[int]. Since the inferred type of bazz is Optional[int], it can later be reassigned None without error.
bazz = None
bazz = 42
bazz = None # Okay
However, if bazz had not been initialized as None, then the following would be an error:
bazz = 42
bazz = None # Error: expression has type "None", variable has type "int"
Inferred types of variables initialized without type annotations
PEP 484 does not explicitly discuss inferring the types of unannotated variables based on the types of their assigned values. However, it can be inferred from the comments in the PEP 484 examples, that the types of unannotated variables are indeed inferred based on assignment.
T = TypeVar('T')
S = TypeVar('S')
class Foo(Generic[T]):
def method(self, x: T, y: S) -> S:
...
x = Foo() # type: Foo[int]
y = x.method(0, "abc") # inferred type of y is str
from typing import TypeVar, Generic
T = TypeVar('T')
class Node(Generic[T]):
x = None # type: T # Instance attribute (see below)
def __init__(self, label: T = None) -> None:
...
x = Node('') # Inferred type is Node[str]
y = Node(0) # Inferred type is Node[int]
z = Node() # Inferred type is Node[Any]