To understand this you will need to understand how self works. You can learn more here:
Understanding self in python
In a nutshell, self refers to the calling object. Invoking self.variable refers to the variable associated with the calling object. Python is smart enough to create one if it doesn't exist.
Calling self.variable inside a class is the same as calling object.variable with your object reference
Consider the following example to prove this:
class Example:
def print_x(self):
print(self.x)
obj = Example()
obj.x = 5; # Create a new attribute of the object and assign it a value 5
print(obj.x) # Outputs 5
obj.print_x() # Outputs 5
In your example, I've added a couple of print statements to help you understand the state of the program during the execution:
class Example:
def __init__(self, name):
print(dir(self)) # Printing object contents before initializing name
self.name = name # New attribute 'name' created
print(dir(self)) # Printing object contents after initializing name
def foo(self):
print("Before foo, self.name = "+ self.name)
self.name = 'John'
print("After foo, self.name = "+ self.name)
bar = Example('Jake')
bar.foo()
print(bar.name)
The output of the above code is
['__doc__', '__init__', '__module__', 'foo']
['__doc__', '__init__', '__module__', 'foo', 'name']
Before foo, self.name = Jake
After foo, self.name = John
John
I will walk you through this code. When we first create bar, the __init__() method is called. Here we print the contents of the object using dir(self). The output ['__doc__', '__init__', '__module__', 'foo'] indicates that the object has only one member, the 'foo' method.
Now we create a new attribute called 'name' and assign it the value 'Jake'. Thus the object now has another member, the 'name' attribute as seen by the output of the next dir(self) ['__doc__', '__init__', '__module__', 'foo', 'name']
Now we call the foo method and print the value before and after the method. Before the name is changed in foo, the value of name associated with the object is "Jake". However, after the name is changed, the value of self.name is "John". This is indicated by
Before foo, self.name = Jake
After foo, self.name = John`
We next verify that the change made by changing self.name has indeed changed the value of name in bar by printing bar.name which gives us the expected output, John
Now coming back to your question, self.name is not an ordinary variable inside some method that is lost when we are out of scope. self can be used essentially anywhere inside the class to refer to the calling object (bar in this case). It is used to manipulate this calling object. Now since bar is within the scope, we are able to print its name attribute.
In normal functions any variables are forgotten but in methods it
seems data is actually appended to the object itself
self manipulates the attributes in the object and is not limited to the scope of the method.