Programming and IT

Python: classes

A class describes what data an object is made of and what it can do. The `__init__` constructor fills in a new object, and `self` is a reference to the object itself. Below: a minimal working class, the difference between class and instance attributes, and ways to make the code shorter.

Updated
In this article

A minimal class#

class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages

    def summary(self):
        return f"{self.title}, {self.pages} pages"

b = Book("War and Peace", 1300)
print(b.summary())     # War and Peace, 1300 pages
print(b.pages)         # 1300

Class names start with a capital letter and run words together (UserProfile) — that is the PEP 8 convention. Calling Book(...) creates an object and passes the arguments to __init__.

What self is and why it comes first#

self is not a keyword but an ordinary first parameter into which Python passes the object itself. Writing b.summary() is equivalent to Book.summary(b). That is why every instance method is declared with self, and data is accessed through it: self.pages, not just pages.

Two mistakes grow from this. The first is forgetting self in the declaration:

class Bad:
    def show():
        return "ok"

Bad().show()
  # TypeError: Bad.show() takes 0 positional arguments but 1 was given

The second is writing return in __init__. The constructor returns nothing; any attempt to return a value other than None raises TypeError.

Class attributes and instance attributes#

An attribute declared directly in the class body belongs to the class and is shared by all objects. An attribute assigned through self belongs to one object.

class Counter:
    total = 0            # shared by all

    def __init__(self):
        self.value = 0   # each object has its own

    def bump(self):
        self.value += 1
        Counter.total += 1

a, c = Counter(), Counter()
a.bump(); a.bump(); c.bump()
print(a.value, c.value, Counter.total)   # 2 1 3

The trap is a mutable class attribute. A list declared in the class body is one list for everyone:

class Cart:
    items = []           # do not do this

x, y = Cart(), Cart()
x.items.append("bread")
print(y.items)           # ['bread']

The right way is to create the list in __init__: self.items = [].

How an object looks when printed#

By default print(obj) gives something like <__main__.Book object at 0x10a3f...>. __repr__ sets a readable form:

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)

print(Point(1, 2))                    # Point(1, 2)
print([Point(0, 0)])                  # [Point(0, 0)]
print(Point(1, 2) == Point(1, 2))     # True

__repr__ is used both when printing the object itself and inside lists, so of the two methods (__str__ and __repr__) start with this one. Without __eq__, two objects with identical data are considered different.

property, classmethod and dataclass#

A computed property is written as a method with the @property decorator and used like an ordinary attribute — without parentheses:

class Rect:
    def __init__(self, w, h):
        self.w, self.h = w, h

    @property
    def area(self):
        return self.w * self.h

r = Rect(3, 4)
print(r.area)     # 12

@classmethod gives you an alternative way to create an object: the first parameter is the class itself, usually called cls. @staticmethod is just a function that lives next to the class. How decorators work in general is explained in the article on Python decorators.

When a class only needs to hold fields, dataclass is shorter: it writes __init__, __repr__ and __eq__ for you.

from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int = 0

u = User("Anna", 30)
print(u)                        # User(name='Anna', age=30)
print(u == User("Anna", 30))    # True

Practice: write a BankAccount class with a balance field, deposit and withdraw methods and an is_empty property; when someone tries to withdraw more than there is, raise ValueError — how to do that is covered in the article on Python exceptions. Inheritance and the other principles are in the article on OOP in Python.

Step-by-step plan

  1. A class with two fieldsDeclare a class, fill the fields in __init__, create two objects and print their fields.
  2. A method that computesAdd a method that uses self and call it on both objects.
  3. Compare attributesAdd a class attribute and an instance attribute, change both and look at the difference.
  4. Readable printingWrite __repr__ and check that the object looks right inside a list.
  5. Shrink it to a dataclassRewrite a data-holding class with @dataclass and compare the amount of code.

Start learning this in your own space

The plan goes into your repository: tick off stages, keep notes — the change history shows how far you have come.

Start the plan

Check yourself

1.Class A has the constructor "def __init__(self, x): self.x = x". What does print(A(5).x) print?

2.Class Cart has the class attribute items = []. After x = Cart(); y = Cart(); x.items.append(1), what is len(y.items)?

3.Which method should you define so that print(obj) shows a readable description of the object?

4.How do you access a property named area that was declared with @property?

Sources

Was this helpful?

More articles

Programming and IT Git branches A Git branch is a movable pointer to a commit, not a copy of your files. That is why creating one costs nothing, and the git branch command is what you use to list, create, rename and delete branches. Programming and IT How to learn Python from scratch Python is a good first programming language: code reads almost like text, and the standard library covers most everyday tasks. This plan takes you from installing the interpreter to your own scripts covered by tests in about four months, at roughly an hour a day. Programming and IT How to create a repository from scratch A repository is your project folder plus a hidden `.git` folder that stores the whole history. One command creates it, but between that command and your first meaningful commit there are a few steps that are hard to redo later — the branch name, the list of ignored files and the link to a server. Programming and IT Python: lists A list is a mutable, ordered collection of values of any type. It is also the main source of surprises for beginners: assignment does not copy a list, sort returns nothing, and removing items inside a loop skips half of them. Let us go through it in order. Programming and IT Python: strings A string in Python is an immutable sequence of characters. Almost all of its quirks follow from that: methods do not change a string but return a new one, and fast text building goes through join, not through adding strings in a loop. Below is the working minimum with examples and output. Programming and IT Python: reading a file Working with a file takes three steps: open it, read or write, close it. You are better off not closing it by hand — that is what the `with` statement is for. And the parameter people forget most is the encoding: without it, the same code reads a file differently on different machines.

More solutions