20

I recently discovered mypy and I want my code to be type checked with it.

I have a Connector base class:

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

And I have several subclasses, they are all connectors, but of different types:

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


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

When I use these objects, I normally put them in a list:

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

Now let's say I want to write a function to return all the short names of all the connectors, as a list. I'd write something like that:

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

When I do that, mypy complains:

error: "Connector" has no attribute "short_name"

Which is true, the base Class Connector doesn't have this attribute, only the subclasses. But all Connector subclasses will have this attribute.

How should I correct that? I can"t use a class attribute here since all my subclasses will need their own short_name attribute.

Should I use a Union in the type hinting of my get_names function (in my real life situation, there are much more than 2 types of connectors, and the user of my API could add his own)?

I'm also not sure I could write a base __init_ function and override it in the subclasses, because the subclasses all have a different init

JPFrancoia
  • 4,866
  • 10
  • 43
  • 73

2 Answers2

21

You'd add that attribute to the base type; you don't need to give it a value:

class Connector:
    short_name: str

This uses Python 3.6's 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).

You can use a comment otherwise, at which point you do have to give the attribute an initial value, and is a class attribute:

class Connector:
   short_name = ''  # type: str
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • But isn't it a class variable? Will all my ```Connector``` objects share it? – JPFrancoia Jul 05 '18 at 12:25
  • 2
    @Rififi Read the PEP linked. `var: Type` is an instance type hint. `var: typing.ClassVar[Type]` is a class type hint. Your instances will share the type hint only, not the values. – FHTMitchell Jul 05 '18 at 12:28
  • @Rififi: I've made it explicit that the annotation documents the type of an instance attribute. – Martijn Pieters Jul 05 '18 at 12:59
5

If you are using python 3.6 or later then

class Connector():
    short_name: str
    ...

should work. This doesn't actually exist in the namespace, but MYPY will find it. See https://www.python.org/dev/peps/pep-0526/.


Another option is to do

import abc
class Connector(abc.ABC):
    @property
    @abc.abstractmethod
    def short_name(self) -> str:
        ...
Rohith Uppala
  • 46
  • 1
  • 9
FHTMitchell
  • 11,793
  • 2
  • 35
  • 47