Are the variables inside __init__ method global?

So I know that a function defined inside a class is called a method. And normally, when we define a function, the variables declared inside the function are only local to that function, right. However, when it comes to class, instance variables defined inside __init__ method can be shared with other methods defined in that class.

For example:

class CurrentAccount:

    def __init__(self, customer_name):
        self.name = customer_name
        self.balance = 0

    def get_customer_name(self):
        return self.name
    
    def get_customer_balance(self):
        return self.balance

account_holder = CurrentAccount("Edward")
print(account_holder.get_customer_name())

get_customer_name() and get_customer_balance() are able to use instance variables from inside the __init__ method. How is it able to use the variables from another method, I thought it’s only local to that method or is it because of __init__, when I remove __init__ or change it to something else, it just returns a TypeError to me.

A standard variable in your function would be more like name = customer_name. What you’re using there, self the typical name used for allowing a created instance object to refer to itself and the dot notation which is a method of adding to, or reading, the attributes of an object. So you’re not just creating a variable, you are directly adding attributes to an object.

You might be familiar with using some of the built-ins using attributes this way, for example list().append() where you make use of attributes that already exist. But you can add new attributes to most objects at almost any time (though this can optionally this can be prevented and many built-ins do so). Some short examples-

class Example():
    name = "test"   # we bind .name attribute to the class object


print(Example.name)  # use the existing attribute
# we can even reassign this attribute name
Example.name = "best"

instance = Example()  # create a new object from our class template
instance.number = 3  # bind a new attribute to our newly created object

So whenever __init__ is actually run you bind two new attributes to that new object. So they’re not names limited to the scope of the function, they’re deliberately bound to a specific object and won’t be immediately forgotten once the function ends.

I should probably add here that whilst you can bind new attributes at almost any point you probably shouldn’t since it can make it very hard to keep track of what object has what attribute. There are valid reasons to do so but wherever possible set up your class attributes in your class definition and instance attributes in the __init__ method.

I’m confused about what you just wrote…So the whole reason why get_customer_name and get_customer_balance are able to access the variables inside __init__ is because of the first parameter of the __init__ method, which in this case is the self instance? Or does Python checks for the local scope or namespace of the instances first and if it’s not found, only then Python checks for the class local scope or namespace? Because as far as I know, a class does not have an enclosing scope.

I’m afraid I can’t quite put my finger on exactly which bit you’re missing here. I would suggest looking into namespace and scope though. The following might help-

__init__ is a method for binding data attributes to newly created instance objects. Typically it runs only once; the reason you can access the values outside this function is because you have deliberately assigned them to another object. This is arguably a side effect, note that __init__ does not accept an explicit return.

# A quick example of side effects
lst = []
def func(word):
    lst.append(word)
func("boo")
print(lst)
Out: ['boo']

So the fact that word is a parameter of func does not matter. Your initialisation step when calling Car("name", "colour") is much the same; your object is created and then __init__ runs, in this case assigning your arguments to the newly created object. We do not pluck values out of the __init__ method itself.

There is also a little confusion regarding what happens when you run a class definition. I would also highly suggests going through the following or a similar guide-
https://docs.python.org/3/tutorial/classes.html

Running a class definition will create a new namespace. This will persist after creation of the class object. The typical route of accessing the names assigned to that class object is object.attribute_name. For an example of this-

class Car:
    a = 1
    b = 2

    def func():
        # used as an example of attributes only...
        # missing the self-referring parameter if creating instances
        pass


# We can access this namespace via the attributes of the created class object
print(Car.a + Car.b)
print(Car.func())
1 Like

Instance variables are global, once you instantiate a class object you can call on its variables anywhere in that file. There’s a separation between the class definition and the object itself, the little guy is born and sent off to live in fields of 1s and 0s. It still kinda calls back to the class for methods. You even get into referencing other objects within the methods of your current object. Objects of the Node class contain instance variables that point to other nodes, and methods that change the properties of other nodes.

Ohh…I think I get it now, is it because the moment I instantiate a class, for example, h1 = Human(), when I did this or at the time of object creation, all instance attributes are bind to the object, and therefore, the program is able to access the attributes defined in another method because all the attributes is bound to that specific object? Am I understanding it correctly, now?

1 Like