mypy: base class 没有属性 x,如何在 base class 中键入提示

mypy: base class has no attribute x, how to type hint in base class

我最近发现了 mypy,我希望用它对我的代码进行类型检查。

我有一个 Connector 基地 class:

class Connector():
    ... some methods, but no __init__ ...

我还有几个子class,都是连接器,只是类型不同:

class Siphon(Connector)
    def __init__():
        short_name = "S"


class Tube(Connector)
    def __init__():
        short_name = "T"

当我使用这些对象时,我通常将它们放在一个列表中:

c1 = Siphon()
c2 = Tube()
list_connectors: List[Connector] = list()
list_connectors.append(c1)
list_connectors.append(c2)

现在假设我想编写一个函数 return 所有连接器的所有短名称,作为一个列表。我会这样写:

def get_names(list_connectors: List[Connector]) -> List[str]:
    tmp_list: List[str] = list()
    for c in list_connectors:
        tmp_list.append(c.short_name)
    return tmp_list

当我这样做时,mypy 抱怨:

error: "Connector" has no attribute "short_name"

没错,基础 Class 连接器没有此属性,只有子 class 有。但是所有的Connector subclasses都会有这个属性。

我该如何纠正?我不能在这里使用 class 属性,因为我所有的子 class 都需要它们自己的 short_name 属性。

我应该在我的 get_names 函数的类型提示中使用 Union 吗(在我的现实生活中,有两种以上的连接器,我的 API 的用户可以添加他自己的)?

我也不确定我是否可以编写一个基础 __init_ 函数并在 subclasses 中覆盖它,因为 subclasses 都有不同的 init

如果您使用的是 python 3.6 或更高版本,那么

class Connector():
    short_name: str
    ...

应该可以。这实际上并不存在于命名空间中,但 MYPY 会找到它。参见 https://www.python.org/dev/peps/pep-0526/


另一种选择是

import abc
class Connector(abc.ABC):
    @property
    @abc.abstractmethod
    def short_name(self) -> str:
        ...

您将该属性添加到基本类型;你不需要给它一个值:

class Connector:
    short_name: str

这使用 Python 3.6 的 Variable Annotation syntax, which is new in Python 3.6 or newer. It defines the type of an instance attribute, not a class attribute (for which there is a separate syntax)。

否则您可以使用注释,此时您必须给属性一个初始值,并且是一个 class 属性:

class Connector:
   short_name = ''  # type: str