Python省略属性未分配,用省略号或None +类型的float提示

时间:2019-12-10 00:08:45

标签: python pycharm type-hinting ellipsis duck-typing

问题

我想声明一个实例属性,类型提示为float,而不在初始化时分配值。该值是在实例初始化后(也称为__init__之后)在运行时分配的。

根据this answer,我可以将值设置为None。但是,我的IDE(PyCharm)产生一个PyTypeChecker标志,说Expected type 'float', got type 'None' instead

我发现是否将值设置为省略号(由this answer表示),又名value = ...,PyCharm不再抱怨。

这里的最佳做法是什么?我应该使用None还是...


示例代码

class SomeClass:

    def __init__(self):
        """Initialize with an unassigned value."""

        # PyCharm complains here
        self._unassigned_val = None  # type: float

        # PyCharm doesn't complain here
        self._unassigned_val2 = ...  # type: float

    def called_during_runtime(self) -> None:
        """This gets called after __init__ has run."""
        self._unassigned_val = 1.0
        self._unassigned_val2 = 1.0

在我的IDE中是什么样子

Sample Code In PyCharm


版本信息

  • PyCharm社区版2019.2.5
  • Python 3.6

1 个答案:

答案 0 :(得分:1)

在简单的情况下,您实际上可以不用在构造函数中执行任何操作:如果执行此操作,则mypy和Pycharm都将继续正确推断字段的类型:

class SomeClass:
    def called_during_runtime(self) -> None:
        self._unassigned_val = 1.0

当然,您需要承担确保在运行时实际调用此函数的责任:类型检查器不会警告您。

如果函数以足够复杂的方式向该字段分配值,则类型检查器可能会阻塞并且不知道该怎么办。在这种情况下,如果您使用的是Python 3.6或更高版本,则可以使用Variable annotations

class SomeClass:
    _unassigned_val: float

    def called_during_runtime(self) -> None:
        self._unassigned_val = 1.0

这完全等同于运行时的第一种方法。

如果您需要支持旧版本的Python,则可以执行的另一种方法是创建一个“伪造”的哨兵值,该值的类型为Any,即完全动态类型:

from typing import Any

BOGUS = object()  # type: Any

class SomeClass:
    def __init__(self) -> None:
        self._unassigned_val = BOGUS  # type: float

    def called_during_runtime(self) -> None:
        self._unassigned_val = 1.0

如果您改变主意并决定偏向于类型检查器以使其警告更加激进,则可以始终声明您的值可以为 float或None:

from typing import Optional

class SomeClass:
    def __init__(self) -> None:
        self._unassigned_val = None  # type: Optional[float]

    def called_during_runtime(self) -> None:
        self._unassigned_val = 1.0

    def get_with_default(self, default: float) -> float:
        if self._unassigned_val is None:
            return default
        else:
            return self._unassigned_val

请注意,然后可以在if语句中使用self._unassigned_val is not Noneself._unassigned_val is Noneisinstance(self._unassigned_val, float)的组合进行检查,并断言使类型检查器有条件地缩小字段的类型。

这最后一种方法是我本人的工作:我非常喜欢类型检查器,并设置我的工具以非常主动地检测潜在问题。

关于省略号的最后一条注解:仅在存根以及使用Protocols方法定义或abstract classes之类的东西时,才将省略号用作占位符-基本上,在您从未真正结束的情况下使用字段的值/方法参数/在运行时进行的任何操作。