If None as a type hint is replaced by type(None) why do they behave differently while annotating?

Viewed 54

I found out from docs:

Note that None as a type hint is a special case and is replaced by type(None).

However, I don't find this to be the case in practice (Using Python 3.8.5, iPython 7.16.1). For example:

  • If I use type(None) to annotate a variable:
In [1]: class Foo:
   ...:    a: int
   ...:    b: str
   ...:    c: type(None)

In [2]: Foo.__annotations__
Out[2]: {'a': int, 'b': str, 'c': NoneType}

In [3]: for k, v in Foo.__annotations.__.items():
   ...:    print(f'Default for {k} -> {v()!r} {v}')

Default for a -> 0 <class 'int'>
Default for b -> '' <class 'str'>
Default for c -> None <class 'NoneType'>
  • Whereas, using simply None produces error:
In [4]: class Foo:
   ...:     a: int
   ...:     b: str
   ...:     c: None

In [5]: Foo.__annotations__
Out[5]: {'a': int, 'b': str, 'c': None}

In [6]: for k, v in Foo.__annotations.__.items():
   ...:    print(f'Default for {k} -> {v()!r} {v}')

Default for a -> 0 <class 'int'>
Default for b -> '' <class 'str'>
Traceback (most recent call last):

  File "<ipython-input-40-d9b0ce31b437>", line 2, in <module>
    print(f'Default for {k} -> {v()!r} {v}')

TypeError: 'NoneType' object is not callable

So what is happening? What does the line from the doc mean? Is it specifically for the linters and does not affect the actual annotations? If so why not enforce type(None) while hinting, or convert None to type(None) in the annotations dict as well, instead of having two different behaviors in almost similar contexts? I guess this is the reason they are bringing back types.NoneType in 3.10?

0 Answers
Related