Closed Xandaros closed 7 years ago
You may have a point. Can you try to find if PEP 484 says it has to be one way or another? If it doesn't we should clarify this there, and if it doesn't we may have to have a larger discussion about whether to change this.
I couldn't find anything about default arguments explicitly, but in The Any
Type, it does say "A function parameter without an annotation is assumed to be annotated with Any
."
I don't know much about how python and its ecosystem is developed - I only use it at work. This is just something I noticed and would prefer to work differently.
I expect @JukkaL can explain the current policy better.
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:
None
doesn't give enough context. With the current rules this doesn't pose a difficulty.''
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.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.
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.
Relevant excerpt from the Pyright docs:
If the type of an unannotated parameter cannot be inferred using any of the above techniques and the parameter has a default argument expression associated with it, the parameter type is inferred from the default argument type. If the default argument is
None
, the inferred type isUnknown | None
.def func(a, b=0, c=None): pass reveal_type(func) # (a: Unknown, b: int, c: Unknown | None) -> None
This inference technique also applies to lambdas whose input parameters include default arguments.
cb = lambda x = "": x reveal_type(cb) # (x: str = "" -> str)
This currently reveals the type as
Any
. I propose it should use the type inference instead and consider thisint
. Perhaps making it optional with a flag, but this seems reasonable to me. I had actually assumed it already worked like this until I decided to verify just now. It is kind of like initialising the variable, is it not?