34

I am using PyCharm to develop some Python app. I am trying to use as cutting-edge python as possible, so I am making use of new python features. I have a problem with type hinting.

Let's all take a look at my DataAnalyzer class:

class DataAnalyzer:

    def __init__(self, train_data: pd.DataFrame, test_data: pd.DataFrame) -> None:
        self.train_data = train_data
        self.test_data = test_data

    def analyze(self):
        pass

Now PyCharm spams me with yellow bulbs wanting me to add type annotations to self.train_data and self.test_data. If I click this message to do so, PyCharm adds two lines at the beginning of my class:

class DataAnalyzer:
    train_data: pd.DataFrame
    test_data: pd.DataFrame

    def __init__(self, train_data: pd.DataFrame, test_data: pd.DataFrame) -> None:
        self.train_data = train_data
        self.test_data = test_data

    def analyze(self):
        pass

I guess now it looks nicer, but AFAIK by writing those variables like this I make them static.

I thought about annotating the types like this:

class DataAnalyzer:

    def __init__(self, train_data: pd.DataFrame, test_data: pd.DataFrame) -> None:
        self.train_data: pd.DataFrame = train_data
        self.test_data: pd.DataFrame = test_data

    def analyze(self):
        pass

Which is definitely not clear, but I am not making my class members static, when I don't want to.

I know, that by having the types annotated in the method signature, doing this one more time when I just assign those, is an overkill, but I am asking for the general rule. Should I annotate those types like PyCharm suggests me to, or should I do this inline?

dabljues
  • 1,663
  • 3
  • 14
  • 30
  • it's weird, which PyCharm version are you using? in my code it works fine with initial approach – Azat Ibrakov Dec 22 '18 at 15:45
  • 13
    By the way, it does not make the members static: https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations – UnholySheep Dec 22 '18 at 15:46
  • @AzatIbrakov it works, but PyCharm suggests me to change it – dabljues Dec 22 '18 at 15:46
  • @UnholySheep so unless I annotate a variables as a `ClassVar` - it's not static, right? – dabljues Dec 22 '18 at 15:47
  • 1
    Yes, `ClassVar` are (static) class variables, while otherwise it's an instance member. Though as described later in the PEP your last approach is also considered a valid convention (possibly related question: https://stackoverflow.com/questions/44959876/type-hints-convention-for-instance-variables-python) – UnholySheep Dec 22 '18 at 15:53
  • Does this answer your question? [Type Hints Convention for Instance Variables Python](https://stackoverflow.com/questions/44959876/type-hints-convention-for-instance-variables-python) – Alonme Mar 21 '20 at 10:10

2 Answers2

29

PyCharm's suggest is right. In fact, I think the following code is better:

class DataAnalyzer:
    train_data: pd.DataFrame
    test_data: pd.DataFrame

    def __init__(self, train_data, test_data):
        self.train_data = train_data
        self.test_data = test_data

    def analyze(self):
        pass

Explain:

  • Annotate a member does not make it static.
  • We should not annotate arguments in the __init__ function again.
  • -> None after __init__ can be omitted. For __init__ never return a value.
feetwet
  • 3,248
  • 7
  • 46
  • 84
胡玉新
  • 306
  • 4
  • 4
  • 5
    Your comment regarding `-> None` contradicts [PEP 484](https://www.python.org/dev/peps/pep-0484/#the-meaning-of-annotations): "Note that the return type of `__init__` ought to be annotated with `-> None`. The reason for this is subtle. If `__init__` assumed a return annotation of `-> None`, would that mean that an argument-less, un-annotated `__init__` method should still be type-checked? Rather than leaving this ambiguous or introducing an exception to the exception, we simply say that `__init__` ought to have a return annotation; the default behavior is thus the same as for other methods." – d4tm4x Jan 18 '21 at 07:58
  • 5
    Why should you not annotate the types of the init parameters? They are carrying the same name as the instance members, but they are in fact different variables. – Sternerson Apr 28 '21 at 08:02
  • 1
    @Sternerson, you are right. Another point to annotate `__init__` parameters is to get prompts from pycharm when you instantiate object (call its constructor). – MrPisarik Jun 07 '21 at 14:45
  • Thanks for this. I definitely think it's easier to read. The only thing is that the variable appears in two places: once at the class definition, once in `__init__` so this makes it slightly morre difficult to change things. – Att Righ Oct 12 '21 at 10:48
  • 1
    I don't consider this method, artificial class attributes with same name as instance attributes, as correct. In fact, you setup both class and instance attributes with same name and I consider it misleading and potentially error prone. Class attributes are used for different purposes. – Karel Marik May 18 '22 at 14:10
  • @karel-marik FYI the type hints do not create class attributes in this example. That only happens if a value is assigned. It's explained in https://peps.python.org/pep-0526/#class-and-instance-variable-annotations "the value-less notation `a: int` allows one to annotate instance variables that should be initialized in `__init__`" Also I tested some sample code similar to this and confirmed that it does not create a class attribute. – Mark Doliner Dec 26 '22 at 20:30
11

Both options are valid - check out pep 526

Type annotations can also be used to annotate class and instance variables in class bodies and methods. In particular, the value-less notation a: int allows one to annotate instance variables that should be initialized in __init__ or __new__.

The MyPy documentation is also a good resource for type hinting

Alonme
  • 1,364
  • 15
  • 28