which seems to be consistent across different python-versions (including python2.7 and python3.10)
Location of the NoneType
has changed across Python versions, see Where is the NoneType located?
The two main ways of getting NoneType
from Python 3.10 onward are calling the builtin type(None)
or importing it, both are equivalent although using type(None)
saves one import:
from types import NoneType
assert type(None) is NoneType
You may need to adjust PyCharm's inspections to be version specific for your project, see Wrong Python version in PyCharm's inspections.
>>> None.__class__
<type 'NoneType'>
Is there a hidden danger I am not seeing?
Using builtin functions, in this case type(), is always preferred. Calling the builtin type()
would also be portable across Python versions.
The alternative of calling the __class__
descriptor directly makes things generally more complicated, see Difference between type(obj) and obj.__class__ and also Python __class__(). Lastly, in typeshed the NoneType
is remarkably simple.
I do not think that this is a PyCharm linter bug but that the warning follows the same trend as mypy, see Warn when NoneType is used in a type #11288 and Treating NoneType as None and warning against using NoneType. #13153.
In effect, using mypy on the snippet:
from types import NoneType
my_var = None.__class__
the_type: NoneType = my_var
gives:
nonetype_test.py:4: error: NoneType should not be used as a type, please use None instead [valid-type]
nonetype_test.py:4: error: Incompatible types in assignment (expression has type "Type[None]", variable has type "None") [assignment]
If instead we use builtins.type
with subscripting (see the end note under the deprecated typing.Type
):
the_type2: type[None] = type(None)
Mypy doesn't complain (and neither does the PyCharm linter):
Success: no issues found in 1 source file