Or can they be declared otherwise?
The code below does not work:
class BinaryNode():
self.parent = None
self.left_child = None
Must all Python instance variables be declared in def __init__?
Short answer: no. But it really depends on how/where to use them.
Or can they be declared otherwise?
Yes they can be declared otherwise (not an instance variable). Since you're referring to variables inside a class, yes you can declare a local variable (for example) inside of it if you don't want to use __init__ constructor function that uses the keyword self.
The code below does not work:
class BinaryNode():
self.parent = None
self.left_child = None
What do you mean by not working? if you're referring to how most developers use a class, then you are right, it does not work in the sense that you can not create a class object (it's also called class instance) and then call one of its instance variables assuming they are declared properly (ie: declared INSIDE the init method that plays the role of the constructor method.)
In other words: if you declare a variable outside the constructor method init then it's called a class variable and not an instance variable. To initiate an instance variable, it needs to be inside the init method that will actually construct (instantiate) the class instance (class object) along with its associated instance variables (instance attributes.)
for example:
if you have parent = None instead of self.parent = None you just created a local variable inside your class.
So the word self is there to hold the place for when you call the class instance is called, it's passed-in in lieu of self. Therefor the word self is just there as a parameter to hold a place for passing whatever instance variable we want to call in its place. So the word self is just doing a favor service here.
Similarly , when creating a class object and say you want to create an instance variable, you must use the attribute word self (it does not have to be self but since most python developers use it, using it became a convention and a good practice in python) that will work as a reference (a holder) to complete the work of the constructor function __init__ which will assign the argument value passed-in the class instance (object) created to be the new value of the instance variable copy that belongs to the class instance created.
Now, let's try another scenario to better solidify the concept:
What if we use the __init__ function but not the keyword self?
if you create a class object named object_x and then you want to call one of its instance variables named instance_y from the , then you execute the program, it will throw an error saying that the class object created does not have an attribute (instance variable).
It will be just a local variable with whatever value you assigned to it when you defined it.
The error occurs because it doesn't recognize it as one of the instance variables defined inside the class when it's missing the reference word self.