23

I sometimes define an object variable outside of __init__. plint and my IDE (PyCharm) complain.

class MyClass():
    def __init__(self):
        self.nicevariable = 1   # everyone is happy

    def amethod(self):
        self.uglyvariable = 2   # everyone complains

plint output:

W:  6, 8: Attribute 'uglyvariable' defined outside __init__ (attribute-defined-outside-init)

Why is this a incorrect practice?

WoJ
  • 27,165
  • 48
  • 180
  • 345

1 Answers1

31

Python allows you to add and delete attributes at any time. There are two problems with not doing it at __init__

  1. Your definitions aren't all in one place
  2. If you use it in a function, you may not have defined it yet

Note that you can fix the above problem of setting an attribute later by defining it in __init__ as:

self.dontknowyet = None      # Everyone is happy
Scarabee
  • 5,437
  • 5
  • 29
  • 55
stark
  • 12,615
  • 3
  • 33
  • 50
  • So it's not unlike how JSLint/JSHint want you to move all your variable declarations to the beginning of their respective functions? – RevanProdigalKnight Aug 06 '14 at 11:25
  • (1) relates to you and more important to everyone also trying to use your code. Be kind, make nice code and put definitions in one place – RvdK Aug 06 '14 at 11:25
  • 11
    The important point being that it makes it much easier to reason about your code. Generally when we think of objects, we thing of a thing with a given set of attributes on it. Having to remember the circumstances under which some of those attributes appear leads to more potential bugs and a harder thing to work with. – Gareth Latty Aug 06 '14 at 11:41
  • I don't think that "Everyone is happy" by setting the attribute as None in the constructor. For example, consider you create a method for a database connection by Psycopg2: `self.cnx = psycopg2.connect(parameters)` then you write `self.cursor = self.cnx.cursor()` and later you want to close this connection and you create a function for closing the connection where you write: `self.cursor.close()` and `self.cnx.close()`. Mypy then complains about: `Mypy: Item "None" of "Optional[Any]" has no attribute "close" [union-attr]`. I agree this is better than not setting it to None in ___init__ though – Banik Oct 10 '22 at 10:03
  • In order to avoid my above-mentioned problem, you should check for None, i.e.: `if self.cursor is not None and self.cnx is not None:`. That's the last piece of the puzzle here. Also when it is possible, you should always set your variable in the constructor, i.e. `df = pd.DataFrame(), my_list = []` ... to provide further type safety and readability. – Banik Oct 10 '22 at 10:06
  • @Banik what if the variable's type is abstract class? – Joey Gao Jul 08 '23 at 06:54