python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.55k stars 2.84k forks source link

Infer argument type from default value #3090

Closed Xandaros closed 7 years ago

Xandaros commented 7 years ago
def test(x:int, y=5):
    reveal_type(y)

This currently reveals the type as Any. I propose it should use the type inference instead and consider this int. 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?

gvanrossum commented 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.

Xandaros commented 7 years ago

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.

gvanrossum commented 7 years ago

I expect @JukkaL can explain the current policy better.

JukkaL commented 7 years ago

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:

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.

gvanrossum commented 7 years ago

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.

tylerlaprade commented 1 year ago

@gvanrossum, add me to the people who care! Pyright and Basedmypy assume the type based on the default value (unless it's None), and it's a huge benefit for me over other typecheckers.

YodaEmbedding commented 3 months ago

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 is Unknown | 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)