What is `self`?

MP 162: It's been answered many times, but it's always worth revisiting.

I've been reflecting on classes lately, and how we teach them. One of the questions that comes up in every discussion about OOP with people who are seeing it for the first time in Python is this:

What is self?

That question has been asked and answered countless times, but I always enjoy revisiting it and coming up with new ways to explain (and show) what it means.

An example class

It's easier to discuss these kinds of things around a concrete example, with a meaningful context. I love birds, so let's write a class that models a bird:

class Bird:

    def __init__(self, name, song="chirp"):
        self.name = name
        self.song = song

    def sing(self):
        print(f"\n{self.song}" * 3)
bird.py

The class Bird lets you create birds that sing. Every instance of Bird has a name, and a song. When you call sing() it repeats its song three times.

Here's a chickadee:

class Bird:
    ...

chickadee = Bird("Chickadee", "chick-a-dee-dee-dee")
chickadee.sing()

So now we have a chickadee that can sing:

$ python3.15 bird.py
chick-a-dee-dee-dee
chick-a-dee-dee-dee
chick-a-dee-dee-dee

What a wonderful class!

Eastern wood-pewee sitting on the rail of a birdfeeder
An Eastern wood-pewee, which we'll make an instance of in a moment.

Where is self?

Let's take a look at where self is in this class.

class Bird:

    def __init__(self, name, song="chirp"):
        self.name = name
        self.song = song

    def sing(self):
        print(f"\n{self.song}" * 3)

In a simple class like this, self shows up in every single line in the class!

I came up with this explanation of what self is a while back, and I still like it:

self is a reference to an instance of the class. If you think of a class as a blueprint for creating individual instances of a thing, self is a reference to one of those specific instances.

If we accept this definition, then inside the class self always refers to a specific instance of Bird. In the bird.py example, that instance is chickadee, but it could be any Bird instance you create.

Much of the magic around self happens in that it's automatically passed to every method in the class. The reference to a specific instance is passed into each method automatically, but you need a parameter in each method to receive that reference. That parameter needs to be the first parameter in the method's signature, and that parameter is almost always named self in Python.

Is self really an instance?

I made this claim: self is a reference to an instance of a class. I like claims that are testable, so let's find a way to test this claim. If you have any question about what self is, I highly recommend that you go through what we're about to do on your own. It will really solidify your understanding of this aspect of how classes work.

Let's put a breakpoint in this bird.py. I'm going to be really intentional about where I place it:

class Bird:

    def __init__(self, name, song="chirp"):
        self.name = name
        self.song = song

    def sing(self):
        print(f"\n{self.song}" * 3)
        breakpoint()


chickadee = Bird("Chickadee", "chick-a-dee-dee-dee")
chickadee.sing()

I want to be able to examine both chickadee and self in a pdb session. If we put the breakpoint at the very end of the program, outside the class, we won't be able to access self. self is only visible while you're inside the class.

I claimed that self is a reference to an instance of the class. The only instance here is chickadee. Let's see if they're the same thing:

$ python3.15 bird.py
chick-a-dee-dee-dee
chick-a-dee-dee-dee
chick-a-dee-dee-dee

> bird.py(9)sing()
-> breakpoint()
(Pdb) self == chickadee
True
(Pdb) self is chickadee
True

At this point in the program, an instance of Bird has been created, which has the name chickadee. We're inside the class, just about to exit the sing() method. When we check for equality, we find that self is indeed equal to chickadee. Even more important, self is chickadee. They both point to exactly the same place in memory.

The claim holds; you could put a breakpoint in __init__(), and find that the self there is also the same thing as chickadee.

Most people recognize that chickadee is an instance of Bird. In the class here, self is the same thing as chickadee, which means it's also a reference to an instance of the class.

What about multiple instances?

Before we leave, let's consider a situation where we have two instances of Bird:

class Bird:

    def __init__(self, name, song="chirp"):
        self.name = name
        self.song = song

    def sing(self):
        print(f"\n{self.song}" * 3)
        breakpoint()


chickadee = Bird("Chickadee", "chick-a-dee-dee-dee")
chickadee.sing()

pewee = Bird("Eastern Wood-Pewee", "pee-a-wee")
pewee.sing()

Now we have a second instance, with the name pewee. When we run bird.py again, the breakpoint should be reached twice. The first time it's reached, during the call to chickadee.sing(), we'll have access to self and chickadee. The second time it's reached, during the call to pewee.sing(), we'll have access to self, chickadee, and pewee.

Let's run this, and examine self each time we hit the breakpoint:

$ python3.15 bird.py
chick-a-dee-dee-dee
chick-a-dee-dee-dee
chick-a-dee-dee-dee

> bird.py(9)sing()
-> breakpoint()
(Pdb) self is chickadee
True

This is the same point we were at in execution earlier. I'll skip the equality tests now, because testing with is is a stronger equality test than using ==. At this point in execution, after calling chickadee.sing(), self is the same thing as chickadee.

If we try to do anything with pewee here, we'll get an error:

(Pdb) pewee
*** NameError: name 'pewee' is not defined

We haven't reached the line that defines pewee yet, so that name doesn't mean anything yet in the pdb session.

Let's continue, and examine self during the call to pewee.sing():

(Pdb) c
pee-a-wee
pee-a-wee
pee-a-wee

> bird.py(9)sing()
-> breakpoint()
(Pdb)

Okay, we're at line 9 again, but now we're at that line after pewee has been created. We can examine self, chickadee, and pewee.

Let's start by running the same comparison we've been running:

(Pdb) self is chickadee
False

This is getting interesting! So far, self has always been a direct reference to chickadee. That's no longer true!

Let's compare self to pewee:

(Pdb) self is pewee
True

self has changed meaning! It's now a reference to pewee.

This is the whole power of self, right here. It's always a reference to the instance of the class that you're currently working with. When you're calling a method through chickadee, ie chickadee.<method_name>(), self is a reference to chickadee and all the values for its attributes such as name and song. When you're calling a method through pewee, ie pewee.<method_name>(), self is a reference to pewee and all its attribute values.

Let's do one last check while we're here. Let's compare chickadee to pewee:

(Pdb) chickadee == pewee
False
(Pdb) chickadee is pewee
False

They're definitely not the same thing. They aren't equal, and they point to different places in memory. That's one of the values of classes; you can create the blueprint of something, capturing the information and behaviors that are common to all instances of that thing. But each instance you create from that class is its own separate thing. If you have a blueprint for a house, you can use that same blueprint to create a hundred different, very separate houses. Those houses all have the same basic structure and functionality, but they are individual houses.

It's important to note that chickadee and pewee are the same type of thing:

(Pdb) type(chickadee)
<class '__main__.Bird'>
(Pdb) type(chickadee) == type(pewee)
True

chickadee is an object of type Bird, in the main program file. chickadee and pewee both share the same type. They're related, but they're not equal.

Conclusions

When you're learning about object-oriented programming, at some point you'll probably feel that you understand the distinction between classes and instances. That understanding will extend to a better understanding of what self refers to. But for a long time, there's room to deepen your understanding of how all these pieces fit together. Playing with short classes, and poking at the variables in a debugging session is a great way to explore and test your understanding of all these parts.

If you enjoyed this discussion, you might enjoy an earlier post that covered the same topic in a different way: What's so special about self?