We haven’t used __init__ above at all. By doing so, we have a chance to understand better the purpose of __init__ in our code .
‘.’ notation is important here. We have already been using ‘.’ notation to access the attribute of object. Recall how we have used object.method() extensively in Python. This object.attribute expression is at the heart of OOP.

__init__ method is run automatically and as soon as we create an instance of a class. In the first example, we have explicitly called setname method while in the second example we actually didn’t call __init__ method explicitly.

Now a word about self.name = who. Here self refer to instance in question. name is the attribute of self. By setname (or __init__) method, we are effectively mapping the name attribute of self to who variable which was passed while defining setname (or __init__) method. Using __init__ also allows us to pass parameters as soon as instance is created. Compare above two program again. Now try running following modified version of first example as shown below –

Observe that we only called the method sayhello explicitly. When we ran this program, method __init__ ran automatically.

One more point. Above we applied method on instance A like this: A.sayhello(). We can also write Person.sayhello(A, 'sam'). In former case, we are using method while in the other case we are using function notation. Observe how function is defined within a class and that self is first parameter passed to this function. So,A.sayhello() is equivalent to Person.sayhello(A, 'Sam')

So what is going on here? See deposit method. First consider self.balance. Though it first appear in __init__ method,we have access to this anywhere in the customer class and that is why we have been able to access in deposit method.
But that is not the case with amount in deposit method. It can only be accessed by deposit. This is local to deposit and can’t be accessed by any other method and that is why the error is thrown. Here self.balance is instance variable and amount is local variable. Run the program again by uncommenting the code lines and deleting the code line print amount in test method.

The only fundamental change we made in this program is that we put double underscore before class variable empCount. In doing so we accomplished something called data hiding. Unlike before, class variable is now inaccessible from outside. And for this reason, the statement print Employee.__empCount didn’t execute as it was trying to access variable from outside.