Self, as you can imagine, is a self-referential parameter. In essence, it's telling the method where data are stored. Let's say we have a class called Dog:
class Dog:
def __init__(self, sound):
self.sound = sound
def bark(self):
return self.sound
When we call the Dog.bark() method, the method needs to know where the data are. Where is this particular dog (named Bacon because I have bacon on the brain right now)? Where can I find Bacon.sound? To satisfy that need, the class passes itself to its methods. Another way to think of if it to imagine a function that takes a Dog as an argument:
def bark(pet):
return pet.sound
Bacon = Dog("woof")
assert Bacon.sound == bark(Bacon)
The Dog.bark() (or Bacon.sound (which is "sizzle", truly)) does the same as the bark() function. It just passes itself into the method. This way, methods can use the same underlying infrastructure as regular functions.
Did that help?