Skip to content
Advertisement

Why are type hints for variables not handled as type hints for function parameters?

When writing a function in Python with type hints like this:

def foo(token: Token=None):
  pass

It translates to this type hint: Optional[Token]. With optional, a None value is accepted too.


When writing the same type hint for a class field, it doesn’t behave the same:

class bar:
  foo: Token = None

Here type hint checkers like the integrated one in PyCharm reports:

Expected type ‘Token’, got None instead.

My questions are:

  • Why is in the parameter case the hint and None implicitly combined to Optional[...]?
  • Why do fields behave differently, while having the same syntax?

I use PyCharm 2019.3.

Advertisement

Answer

Why is in the parameter case the hint and None implicitly combined to Optional[…]

This is how PyCharm’s static type checker infers the type of arguments when their default values that are set to None. It infers the type to become Optional[type] of what you declared. This inference rule is applied differently for variable declarations outside of signatures.

Why do fields behave differently, while having the same syntax?

From what we are given to understand from the below sources this was a syntax detail choice aimed at making signatures more concise although introducing an implicit rule.

The way PyCharm’s static type checker is implemented aligns with historical PEP 484 changes. The current revision states:

Union types – PEP 484

A past version of this PEP allowed type checkers to assume an optional type when the default value is None, as in this code:

def handle_employee(e: Employee = None): ...

This would have been treated as equivalent to:

def handle_employee(e: Optional[Employee] = None) -> None: ...

This is no longer the recommended behavior. Type checkers should move towards requiring the optional type to be made explicit.

Looking at the revision history “PEP 484: Do not require type checkers to treat a None default special…” the reasons are given in Pull request #689 linking back to typing issue #275.

I altered your code, because Token was undefined, to:

def foo(token: int = None) -> None:
  pass

class bar:
  foo: int = None

Using the mypy static type checker with default configurations the warnings are issued as expected:

error: Incompatible default for argument "token" (default has type "None", argument has type "int")
error: Incompatible types in assignment (expression has type "None", variable has type "int")

I didn’t find any PyCharm inspection having a configurable option for this.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement