The magic of self: how python inserts self into methods - Sebastiaan Zeeff

Tags: python, pun

(One of my summaries of the 2023 Dutch pythonconferentie python meeting in Utrecht, NL).

There’s deep magic in python. Some of it is really hard to understand. Guido van Rossum: “from day one, there was deep magic hiding in some places, designed to quietly help users”. Sebastiaan is going to show us a nice example of python magic: how “self” gets injected into method calls on a class:

class Guitar:
    def __init__(self, name): = name

    def play_note(self, note):
        print(f"{} plays {note}")

You can instantiate a guitar and call my_guitar.play_note("B") on it. With just the note as a parameter. But where is the self coming from? Somehow your instance of the Guitar class is magically inserted.

Note: self is not a magical keyword. You can give it a different name and it will still work (though your code will be unreadable and people will hate you).

Everything in python is an object. A class is an object (an instantiation of Type). A method inside a class is a regular function object. Plus it is also added with the name of the function as an attribute to the class (the namespace of the class).

If you call my_guitar.play_note("B"), your my_guitar is just an object. It does not have the attribute pointing at the function in its namespace. But it does find the function, as python also looks up attributes in the class of an object: this is what “self” comes from.

You can see it when you look at the method: Guitar.play_note says it is a “function”. my_guitar.play_note says it is a “bound function”.

The magic that is happening is done with “dunder methods”:

  • __get__ customizes attribute lookup.

  • __set__ is for setting attributes.

  • __delete__ is for deleting.

Functions all have a __get__() method. When accessed on a class, you’ll get the function back. When accessed on an instance, it returns a PyMethod, which is the function bound to the instance where the instance is passed as the first argument.

There are some decorators that are related to this:

  • @classmethod binds the function to the class, not the instance.

  • @staticmethod returns the function as-is, without any binding.

  • @property lets you easily customize getters and setters for the attribute.

These “descriptors” allow you to customize how attributes work.

Book recommendation: “fluent python”. logo

About me

My name is Reinout van Rees and I work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):