Instance variables in methods outside the constructor (Python) -- why and how?

14,927

Solution 1

Why is it best practice to initialize the instance variable within the constructor?

Clarity.

Because it makes it easy to see at a glance all of the attributes of the class. If you initialize the variables in multiple methods, it becomes difficult to understand the complete data structure without reading every line of code.

Initializing within the __init__ also makes documentation easier. With your example, you can't write "an instance of Cat has a roar attribute". Instead, you have to add a paragraph explaining that an instance of Cat might have a "roar" attribute, but only after calling the "meow_louder" method.

Clarity is king. One of the smartest programmers I ever met once told me "show me your data structures, and I can tell you how your code works without seeing any of your code". While that's a tiny bit hyperbolic, there's definitely a ring of truth to it. One of the biggest hurdles to learning a code base is understanding the data that it manipulates.

What general/specific mess could arise if instance variables are regularly initialized in methods other than the constructor?

The most obvious one is that an object may not have an attribute available during all parts of the program, leading to having to add a lot of extra code to handle the case where the attribute is undefined.

In what scenarios would it be better to initialize instance variables in the other methods, rather than in the constructor?

I don't think there are any.

Note: you don't necessarily have to initialize an attribute with it's final value. In your case it's acceptable to initialize roar to None. The mere fact that it has been initialized to something shows that it's a piece of data that the class maintains. It's fine if the value changes later.

Solution 2

Remember that class members in "pure" Python are just a dictionary. Members aren't added to an instance's dictionary until you run the function in which they are defined. Ideally this is the constructor, because that then guarantees that your members will all exist regardless of the order that your functions are called.

I believe your example above could be translated to:

class Cat():
    def __init__(self):
        self.__dict__['meow'] = "Meow!"
    def meow_bigger(self):
        self.__dict__['roar'] = "Roar!"

>>> c = Cat()        # c.__dict__ = { 'meow': "Meow!" }
>>> c.meow_bigger()  # c.__dict__ = { 'meow': "Meow!", 'roar': "Roar!" }

Solution 3

To initialize instance variables within the constructor, is - as you already pointed out - only recommended in python.

First of all, defining all instance variables within the constructor is a good way to document a class. Everybody, seeing the code, knows what kind of internal state an instance has.

Secondly, order matters. if one defines an instance variable V in a function A and there is another function B also accessing V, it is important to call A before B. Otherwise B will fail since V was never defined. Maybe, A has to be invoked before B, but then it should be ensured by an internal state, which would be an instance variable.

There are many more examples. Generally it is just a good idea to define everything in the __init__ method, and set it to None if it can not / should not be initialized at initialization.

Of course, one could use hasattr method to derive some information of the state. But, also one could check if some instance variable V is for example None, which can imply the same then. So in my opinion, it is never a good idea to define an instance variable anywhere else as in the constructor.

Your examples state some basic properties of python. An object in Python is basically just a dictionary. Lets use a dictionary: One can add functions and values to that dictionary and construct some kind of OOP. Using the class statement just brings everything into a clean syntax and provides extra stuff like magic methods.

In other languages all information about instance variables and functions are present before the object was initialized. Python does that at runtime. You can also add new methods to any object outside the class definition: Adding a Method to an Existing Object Instance

Solution 4

3.) But instance variables can also be defined/initialized outside the constructor, e.g. in the other methods of the same class.

I'd recommend providing a default state in initialization, just so its clear what the class should expect. In statically typed languages, you'd have to do this, and it's good practice in python.

Let's convey this by replacing the variable roar with a more meaningful variable like has_roared.

In this case, your meow_bigger() method now has a reason to set has_roar. You'd initialize it to false in __init__, as the cat has not roared yet upon instantiation.

class Cat():
    def __init__(self):
        self.meow = "Meow!"
        self.has_roared = False

    def meow_bigger(self):
         print self.meow + "!!!"
         self.has_roared = True

Now do you see why it often makes sense to initialize attributes with default values?

All that being said, why does python not enforce that we HAVE to define our variables in the __init__ method? Well, being a dynamic language, we can now do things like this.

>>> cat1 = Cat()
>>> cat2 = Cat()
>>> cat1.name = "steve"
>>> cat2.name = "sarah"
>>> print cat1.name
... "steve"

The name attribute was not defined in the __init__ method, but we're able to add it anyway. This is a more realistic use case of setting variables that aren't defaulted in __init__.

Solution 5

I try to provide a case where you would do so for:

3.) But instance variables can also be defined/initialized outside the constructor, e.g. in the other methods of the same class.

I agree it would be clear and organized to include instance field in the constructor, but sometimes you are inherit other class, which is created by some other people and has many instance fields and api.

But if you inherit it only for certain apis and you want to have your own instance field for your own apis, in this case, it is easier for you to just declare extra instance field in the method instead override the other's constructor without bothering to deep into the source code. This also support Adam Hughes's answer, because in this case, you will always have your defined instance because you will guarantee to call you own api first.

For instance, suppose you inherit a package's handler class for web development, you want to include a new instance field called user for handler, you would probability just declare it directly in the method--initialize without override the constructor, I saw it is more common to do so.

class BlogHandler(webapp2.RequestHandler):
     def initialize(self, *a, **kw):
         webapp2.RequestHandler.initialize(self, *a, **kw)
         uid = self.read_cookie('user_id') #get user_id by read cookie in the browser
         self.user = User.by_id(int(uid)) #run query in data base find the user and return user
Share:
14,927

Related videos on Youtube

xax
Author by

xax

Updated on June 04, 2022

Comments

  • xax
    xax almost 2 years

    My questions concern instance variables that are initialized in methods outside the class constructor. This is for Python.

    I'll first state what I understand:

    1. Classes may define a constructor, and it may also define other methods.
    2. Instance variables are generally defined/initialized within the constructor.
    3. But instance variables can also be defined/initialized outside the constructor, e.g. in the other methods of the same class.
    4. An example of (2) and (3) -- see self.meow and self.roar in the Cat class below:

      class Cat():
      
          def __init__(self):
              self.meow = "Meow!"
          def meow_bigger(self):
              self.roar = "Roar!"
      

    My questions:

    1. Why is it best practice to initialize the instance variable within the constructor?

    2. What general/specific mess could arise if instance variables are regularly initialized in methods other than the constructor? (E.g. Having read Mark Lutz's Tkinter guide in his Programming Python, which I thought was excellent, I noticed that the instance variable used to hold the PhotoImage objects/references were initialized in the further methods, not in the constructor. It seemed to work without issue there, but could that practice cause issues in the long run?)

    3. In what scenarios would it be better to initialize instance variables in the other methods, rather than in the constructor?


    1. To my knowledge, instance variables exist not when the class object is created, but after the class object is instantiated. Proceeding upon my code above, I demonstrate this:

      >> c = Cat() 
      >> c.meow
      'Meow!'
      >> c.roar
      Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
      AttributeError: 'Cat' object has no attribute 'roar'
      >>> c.meow_bigger()
      >>> c.roar
      'Roar!'
      

      As it were:

      • I cannot access the instance variable (c.roar) at first.
      • However, after I have called the instance method c.meow_bigger() once, I am suddenly able to access the instance variable c.roar.
      • Why is the above behaviour so?

    Thank you for helping out with my understanding.

  • xax
    xax almost 8 years
    I learned something new. Did not know that class members were a dictionary. Thank you!
  • BlackHat
    BlackHat about 3 years
    This is the best way to avoid code smell, and ensure readability and clean code.
  • mattsmith5
    mattsmith5 about 3 years
    hi , I left a question here, maybe you can answer it, thanks stackoverflow.com/questions/66937061/…
  • mattsmith5
    mattsmith5 about 3 years
    hi , I left a question here, maybe you can answer it, thanks stackoverflow.com/questions/66937061/…