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:
- Classes may define a constructor, and it may also define other methods.
- Instance variables are generally defined/initialized within the constructor.
- But instance variables can also be defined/initialized outside the constructor, e.g. in the other methods of the same class.
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:
Why is it best practice to initialize the instance variable within the constructor?
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?)
In what scenarios would it be better to initialize instance variables in the other methods, rather than in the constructor?
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.
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:
Let me try to shed some light on this specific issue:
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 likehas_roared
.In this case, your
meow_bigger()
method now has a reason to sethas_roar
. You'd initialize it to false in__init__
, as the cat has not roared yet upon instantiation.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.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__
.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 functionA
and there is another function B also accessingV
, it is important to callA
beforeB
. OtherwiseB
will fail since V was never defined. Maybe,A
has to be invoked beforeB
, 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 toNone
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 variableV
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
i think to keep it simple and understandable, better to initialize the class variables in the class constructor, so they can be directly called without the necessity of compiling of a specific class method.
Output
Roar!
Roar!
Meow!Roar!
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 aroar
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.
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.
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
toNone
. 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.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.