Reputation: 3831
What is the type (in the sense of type annotations, not type()
) of variables and parameters that come without a type annotation but with an initial value? E.g.,
foo = 2
bar = False
baz = MyClass()
bazz = None
Judging from the Mypy docs and example code in the Python docs, foo
, bar
, baz
would currently be assigned the types int
, False
and MyClass
. Has this been standardized anywhere, though? And what about bazz
? And what about
def my_func(param1 = 2, param2 = False):
...
? Would a type checker enforce arguments passed to my_func
to be of type int
and bool
, respectively?
Note that I'm not that much interested in the status quo, e.g. the current implementation of type checkers like Mypy. Rather, I'm wondering whether the answer to my questions has been standardized anywhere. Unfortunately, PEP-484 does not seem to say anything about this, apart from:
Type checkers are expected to attempt to infer as much information as necessary.
…but it is unclear what this really means in the above situation. (After all, param1 = 2
might only be the default value and its actual type might be more complex in reality.)
Upvotes: 3
Views: 2034
Reputation: 24154
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 beUnion[str, unicode]
. If the default is0
, the correct type might well befloat
. 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
forx
. 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.
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"
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]
Upvotes: 4
Reputation: 6026
Concerning the types situation of this block
foo = 2
bar = False
baz = MyClass()
bazz = None
they do have types that you could annotate if you want them to be checked. In my experience, there are certain cases where mypy
manages to infer some types, but usually is because they're being annotated somewhere else "earlier"like in function signatures of attributes in classes or instances.
Thus, the block
foo: int = 2
bar: bool = False
baz: MyClass = MyClass()
bazz: Optional[?] = None
In the case of baz
the class itself is a type, so you annotate it with the name of the class.
For bazz
I left a ?
since you should have an expectation of the type it'll have. Maybe it can be a couple of possible types and use Union[int, float]
or maybe you don't really know/care and use Any
as type.
Now, about the function
def my_func(param1 = 2, param2 = False):
...
If the signature of that function would be annotated, the type checker would tell you if it's being called correctly.
def my_func(param1: int = 2, param2: bool = False):
...
The type checker is not an enforcer, python continues being a dynamic language. Although there could be tools/libraries that exploit the annotations to do some runtime enforcement of types.
Upvotes: 0