Python 3 OOP wrapping my head

Hiya

First off, massive fan of the python 3 book. I’m currently on ex_43 and I’m finding it difficult to wrap my head around Object Oriented programming and have a question. Before i get asking, I’ve gone over the has-a, is-a phrase drill program quite a bit and seem to get a grasp of it.

In ex_36 where we made a text based game I felt really comfortable with it. I was able to use functions, lists, loops and if -statements without any issues to make a game with many decisions and even a player inventory where the player can press keys 1-6 to use items/abilities.

When we take that same concept into OOP in ex_43 I feel completely overwhelmed. So a couple questions as follows.

  1. What is the purpose of the self parameter and why is it only used on some functions?
    example to elaborate:

     class Person(object):
         def __init__(self, name):
             ## class person has-a __init__ function with the self, name parameters
             self.name = name
             ## Person has-a pet of some kind
             self.pet = None
    

this example is from ex_42. So i thought we use parameters such as the name in this case so we can use a variable outside of the function to be used inside. also where did .pet come from and how can it be used if it’s not defined within the function?

I’m probably not grasping the basics here. My apologies if this was discussed earlier in the book.

This link might help if you want a technical explanation: https://docs.python.org/3/faq/design.html#why-must-self-be-used-explicitly-in-method-definitions-and-calls

But basically it helps to define if you are referring to an instance or local variable by being explicit.

The class Person() has a name (that is captured upon instantiation from outside as you mention). But the class Person() may also have a pet. That is an attribute of the Person class (so an instance variable) that applies only to ‘that’ version of the class when in use. So adding .self helps with this.

(You set it to ‘None’ by default so it can be overridden later, but you know that a pet instance will apply to every version of the class that is instantiated).

Most of the problems with OOP come from over complicating things so let me try a few simple examples. First, look at self:

x = Person("Joe")
y = Person("Frank")

Now, the code Person("Joe") creates a Person right? But how does it do that? Well it runs the __init__ function so that you have a chance to configure everything. In this case you’re going to set self.name = "Joe".

Why use self? Well, your __init__ and all the other functions in this class need to know what variable they’re working with. Is a call to __init___ talking about x or y in my example above? Your code can’t possibly anticipate every name you’re going to give an object, and Python actually doesn’t know the name at that point. It’s still just creating Joe or Frank before it gets assigned to x or y.

What self does is allows your code to operate on any variable without having to know the name. It just means “whatever object I’m currently using”. In the first line, self is set to x. In the second self is set to y. If you had 1 million variables your code would still only use self, and each of these million variables would get assigned to self.

Now, pet is optional, so all you have to do if someone has a pet is set it like this:

x = Person("Joe")
x.pet = Pet("Scruffy") # joe has a pet
y = Person("Frank") # Frank does not

After your __init__ exits Python takes whatever you were calling self and assigns it to x or y. Then after that you do to x and y the same thing you do to self. Just use the . (period) to access the pet, or functions, or anything else.

Finally, try this:

print(x.__dict__)

That’ll show you the guts of your objects.

Thank you so much. I pushed through it some more and it makes perfect sense now!

1 Like