Программирование и IT

Python: классы

Класс описывает, из каких данных состоит объект и что он умеет делать. Конструктор `__init__` заполняет новый объект, `self` — ссылка на него самого. Ниже — минимальный рабочий класс, разница между атрибутом класса и атрибутом экземпляра и способы сделать код короче.

Обновлено
В этой статье

Минимальный класс#

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

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

b = Book("Война и мир", 1300)
print(b.summary())     # Война и мир, 1300 с.
print(b.pages)         # 1300

Имя класса пишут с заглавной, слова слитно (UserProfile) — так принято в
PEP 8. Вызов Book(...) создаёт объект и передаёт аргументы в __init__.

Что такое self и почему он первый#

self — не ключевое слово, а обычный первый параметр, в который Python
подставляет сам объект. Запись b.summary() равнозначна Book.summary(b).
Поэтому каждый метод экземпляра объявляют с self, а обращение к данным идёт
через него: self.pages, а не просто pages.

Две ошибки растут отсюда. Первая — забыть self в объявлении:

class Bad:
    def show():
        return "ок"

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

Вторая — писать в __init__ return. Конструктор ничего не возвращает; любая
попытка вернуть значение, кроме None, даёт TypeError.

Атрибут класса и атрибут экземпляра#

Атрибут, объявленный прямо в теле класса, принадлежит классу и общий для всех
объектов. Атрибут, присвоенный через self, принадлежит одному объекту.

class Counter:
    total = 0            # общий для всех

    def __init__(self):
        self.value = 0   # свой у каждого

    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

Ловушка — изменяемый атрибут класса. Список, объявленный в теле класса, один на
всех:

class Cart:
    items = []           # так делать не надо

x, y = Cart(), Cart()
x.items.append("хлеб")
print(y.items)           # ['хлеб']

Правильно создавать такой список в __init__: self.items = [].

Как объект выглядит при печати#

По умолчанию print(obj) даёт что-то вроде <__main__.Book object at 0x10a3f...>. Читаемый вид задаёт __repr__:

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__ используется и при печати самого объекта, и внутри списков, поэтому
из двух методов (__str__ и __repr__) начинать стоит именно с него. Без
__eq__ два одинаковых по данным объекта считаются разными.

property, classmethod и dataclass#

Вычисляемое свойство пишется как метод с декоратором @property, а
используется как обычный атрибут — без скобок:

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 даёт альтернативный способ создать объект: первый параметр —
сам класс, обычно его называют cls. @staticmethod — просто функция,
живущая рядом с классом.

Когда класс нужен только для хранения полей, короче взять dataclass: он сам
напишет __init__, __repr__ и __eq__.

from dataclasses import dataclass

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

u = User("Аня", 30)
print(u)                       # User(name='Аня', age=30)
print(u == User("Аня", 30))    # True

Практика: опиши класс BankAccount с полем баланса, методами пополнения и
списания и свойством is_empty; при попытке снять больше, чем есть, бросай
ValueError — как это делается, разобрано в статье про
исключения. Наследование и остальные принципы —
в статье про ООП.

План по этапам

  1. Класс с двумя полямиОбъявить класс, заполнить поля в __init__, создать два объекта и вывести их поля.
  2. Метод, который считаетДобавить метод, использующий self, и вызвать его у обоих объектов.
  3. Сравнить атрибутыЗавести атрибут класса и атрибут экземпляра, изменить оба и посмотреть на разницу.
  4. Читаемая печатьНаписать __repr__ и убедиться, что объект нормально выглядит внутри списка.
  5. Сократить до dataclassПереписать класс-хранилище через @dataclass и сравнить объём кода.

Начать изучать эту тему у себя

План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.

Начать план

Проверь себя

1.У класса A конструктор «def __init__(self, x): self.x = x». Что выведет print(A(5).x)?

2.Класс Cart имеет атрибут класса items = []. После x = Cart(); y = Cart(); x.items.append(1) чему равно len(y.items)?

3.Какой метод нужно определить, чтобы print(obj) показывал читаемое описание объекта?

4.Как обратиться к свойству, объявленному с @property, по имени area?

Источники

Было полезно?

Ещё темы

Программирование и IT Как создать репозиторий с нуля Репозиторий — это папка проекта плюс скрытая папка `.git`, в которой хранится вся история. Создаётся одной командой, но между ней и первым осмысленным коммитом есть несколько шагов, которые потом трудно переделать: имя ветки, список игнорируемых файлов, привязка к серверу. Программирование и IT Как изучить Python с нуля Python хорош для первого знакомства с программированием: код читается почти как текст, а стандартная библиотека закрывает большинство бытовых задач. Этот план ведёт от установки интерпретатора до собственных скриптов, покрытых тестами, — примерно за четыре месяца при занятиях около часа в день. Программирование и IT git branch Ветка в git — это подвижный указатель на коммит, а не копия файлов. Поэтому завести её ничего не стоит, а команда git branch отвечает за список веток, их создание, переименование и удаление. Программирование и IT Python: декораторы Декоратор — это функция, которая принимает другую функцию и возвращает новую, с добавленным поведением. Знак `@` перед определением — просто короткая запись присваивания. Если держать это в голове, вся тема разбирается за один вечер. Программирование и IT Python: функции Функция — именованный кусок кода, который принимает значения и возвращает результат. В Python объявление занимает одну строку, но у аргументов есть тонкости: умолчания вычисляются один раз, именованные и позиционные смешиваются по правилам, а без return функция всё равно что-то возвращает. Программирование и IT Python: чтение файла Работа с файлом состоит из трёх шагов: открыть, прочитать или записать, закрыть. Закрытие лучше не делать руками — для этого есть оператор `with`. А главный параметр, про который забывают, — кодировка: без неё один и тот же код на разных машинах читает файл по-разному.

Ещё сценарии