Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a default parameters overwrite type hints for mypy?

The following code is rejected by mypy as expected:

def foo(value: int) -> None:
    print(value, type(value))
foo(None)

output:

error: Argument 1 to "foo" has incompatible type "None"; expected "int"

But after introducing a default parameter of None, there is no error anymore:

def foo(value: int=None) -> None:
    print(value, type(value))
foo(None)

I would expect mypy to only allow None (as argument and as the default) if we change value from int to Optional[int], but it seems like this is not needed. Why?

like image 948
Tobias Hermann Avatar asked Jun 27 '18 06:06

Tobias Hermann


1 Answers

When you make a keyword argument accept None, mypy will implicitly make that argument be of type Optional[Blah] if it isn't already. You can see this by adding the reveal_type(...) function to your code and running mypy:

def foo(value: int = None) -> None:
    print(value, type(value))

reveal_type(foo)
foo(None)

The output would be:

test.py:4: error: Revealed type is 'def (value: Union[builtins.int, None] =)'

(Be sure to delete reveal_type before actually running your code though, since the function doesn't actually exist at runtime -- it's just special-cased by mypy to help with debugging.)

This behavior exists mostly because it helps makes the signatures of functions less noisy. After all, if value, at some point, is allowed to be None, clearly it must accept both ints and None. In that case, why not just infer the type to be Optional[int] (which is equivalent to Union[int, None], btw) so the user doesn't need to repeat the same info twice?

Of course, not everybody likes this behavior: some people prefer being more explicit. In that case, run mypy with the --no-implicit-optional flag. That will produce the following output:

test.py:1: error: Incompatible default for argument "value" (default has type "None", argument has type "int")
test.py:4: error: Revealed type is 'def (value: builtins.int =)'
test.py:5: error: Argument 1 to "foo" has incompatible type "None"; expected "int"

You'd need to change your function signature though, of course.

If you'd like to raise the strictness of mypy in various other ways, try passing the --strict flag. That will automatically enable --no-implicit-optional and several other strictness flags. For more details, run mypy --help.

like image 168
Michael0x2a Avatar answered Oct 08 '22 08:10

Michael0x2a