Skip to content
Theory · Classes · lesson 1 of 11

Classes and objects

What you'll learn

What you'll learn:

  • describe your own data type with a class and create objects from it;
  • explain what self is and why a method has one more parameter than its call;
  • set how an object prints and compares with __str__, __repr__ and __eq__;
  • tell a class attribute from an instance attribute, inherit classes and write data classes.
A blueprint Player on the wall and two cards filled in from it, the first one with the name Ann

Picture a blueprint on the wall. It says Player and lists the fields: a name and a score. Player cards are filled in from the blueprint: the first one says Ann, the second Bob. There is one blueprint and any number of cards, and each card has its own values.

In Python, such a blueprint is called a class. But first, let's see how to manage without one. Player data can be kept in a dict, with the actions on it written as separate functions:

Python
def add_points(player, n):
    player["score"] += n


ann = {"name": "Ann", "score": 0}
add_points(ann, 5)
print(ann["name"], ann["score"])  # Ann 5

The program works, but it has two weak spots:

  • which keys a player has is visible only where the dict is created;
  • the add_points function accepts any dict, even one without the "score" key.

Python lets you describe a player in one place: what data it has and what can be done with it.

Class and instances

A class is a description of a new type of object, that very blueprint. An object of that type is called an instance of the class: a filled-in card. Variables tied to an instance are its attributes, the fields of the card. Functions declared inside the class are its methods, the actions on the card.

Python
class Player:
    def __init__(self, name):
        self.name = name
        self.score = 0

    def add_points(self, n):
        self.score += n


ann = Player("Ann")
ann.add_points(5)
print(ann.name, ann.score)  # Ann 5

Let's take apart what happens in the line ann = Player("Ann"):

  1. The call Player("Ann") creates a new empty object, a blank card.
  2. Python calls its __init__ method: self receives that object, and name receives the string "Ann".
  3. The lines self.name = name and self.score = 0 create the instance's attributes.
  4. The finished object becomes the result of the call, and the name ann refers to it.

Not every attribute comes from a parameter: every new player's score starts at 0. There is no need to return the object from __init__: the result of the call Player("Ann") is the object itself.

Class names start with a capital letter and have no underscores: Player, BankAccount. Methods are separated by one blank line.

The self parameter

Look at the call ann.add_points(5). There is one argument in the parentheses, but the method has two parameters. Python adds the other argument itself: it passes the object before the dot first, into self.

The call ann.add_points(5) means the same as Player.add_points(ann, 5). The method is stored once in the class, shared by everyone, and self tells it whose card to work with. That is why self.score += n changes the score of exactly the instance the method was called on.

Press "Next step" and watch the add_points frame. In the first call self is ann, in the second it is bob, and only that object's score changes.

LiveOne method, two objectsstep 1 of 13 · frames: 1

The call Player("Ann") creates a new object and passes it to __init__.

Watch which object is in self

Each call of the class creates a separate instance with its own attributes. Attributes are read and changed through a dot from outside the class too:

Python
bob = Player("Bob")
bob.add_points(2)
print(ann.score, bob.score)  # 5 2
bob.score = 10
print(ann.score, bob.score)  # 5 10

This is where the card analogy stops working. The method is not written on each card: it is stored once in the class, on the blueprint. Each time, it gets the card to work with through self.

Interviewers like to ask: "What is self?" The answer has three parts:

  • self is a method's first parameter, and it holds the object the method was called on;
  • Python passes it itself: ann.add_points(5) is Player.add_points(ann, 5);
  • self is not a keyword but a naming convention, yet it is always written.

Common mistake

The self parameter is left out of a method's declaration:

Python
class Player:
    def __init__(self, name):
        self.name = name
        self.score = 0

    def add_points(n):  # ✗
        self.score += n


ann = Player("Ann")
ann.add_points(5)
TEXT
TypeError: Player.add_points() takes 1 positional argument but 2 were given

In the call ann.add_points(5), Python passes two arguments, ann and 5, while the method takes one. Write self as the first parameter of every method.

Key points

  • A class describes a new type of object, and each call Player("Ann") creates a separate instance.
  • __init__ receives the new object in self and creates its attributes.
  • ann.add_points(5) is Player.add_points(ann, 5): the object before the dot goes into self.
  • A method is shared by all instances, while each has its own attributes.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way