Добавить в корзинуПозвонить
Найти в Дзене
Код как искусство

Классы и объекты: как перестать писать функции-одиночки и объединить данные с действиями

Вы уже умеете писать функции, использовать списки и словари, обрабатывать ошибки и даже собирать код в модули. Но есть одна вещь, которая отличает скрипты от настоящих программ — объектно-ориентированное программирование (ООП). Сегодня мы познакомимся с классами и объектами. Не пугайтесь — это просто способ упаковать данные и действия в одну красивую коробку. Представьте, что вы пишете программу для учёта студентов. У каждого студента есть имя, возраст, оценки. Без классов вам придётся хранить три списка и передавать их в каждую функцию: names = ["Анна", "Иван"]
ages = [20, 21]
grades = [[5,4,5], [3,4,4]]
def add_grade(name, grade):
# найти индекс имени, потом добавить оценку... Это работает, но становится неудобно, когда данных много. Класс позволяет объединить имя, возраст, оценки и методы работы с ними в одну сущность — объект. Синтаксис класса: class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
pr
Оглавление

Вы уже умеете писать функции, использовать списки и словари, обрабатывать ошибки и даже собирать код в модули. Но есть одна вещь, которая отличает скрипты от настоящих программ — объектно-ориентированное программирование (ООП). Сегодня мы познакомимся с классами и объектами. Не пугайтесь — это просто способ упаковать данные и действия в одну красивую коробку.

1. Почему функций уже недостаточно?

Представьте, что вы пишете программу для учёта студентов. У каждого студента есть имя, возраст, оценки. Без классов вам придётся хранить три списка и передавать их в каждую функцию:

names = ["Анна", "Иван"]
ages = [20, 21]
grades = [[5,4,5], [3,4,4]]

def add_grade(name, grade):
# найти индекс имени, потом добавить оценку...

Это работает, но становится неудобно, когда данных много. Класс позволяет объединить имя, возраст, оценки и методы работы с ними в одну сущность — объект.

2. Класс — это чертёж, объект — конкретный экземпляр

  • Класс — как форма для выпечки. Она описывает, какой формы будут печенья, но сама по себе не съедобна.
  • Объект — конкретное печенье, созданное по этой форме.

Синтаксис класса:

class Student:
def __init__(self, name, age):
self.name = name
self.age = age

def say_hello(self):
print(f"Привет, я {self.name}, мне {self.age} лет.")

Разберём:

  • __init__ — конструктор, вызывается при создании объекта. self — ссылка на сам объект.
  • self.name = name — сохраняем данные внутрь объекта.
  • say_hello — метод (функция внутри класса).

Создаём объекты:

student1 = Student("Анна", 20)
student2 = Student("Иван", 21)

student1.say_hello() # Привет, я Анна, мне 20 лет.
student2.say_hello() # Привет, я Иван, мне 21 год.

3. Атрибуты и методы

  • Атрибуты — данные, принадлежащие объекту (например, name, age).
  • Методы — функции, работающие с этими данными.

Добавим метод для добавления оценки:

class Student:
def __init__(self, name, age):
self.name = name
self.age = age
self.grades = [] # список оценок

def add_grade(self, grade):
self.grades.append(grade)

def average_grade(self):
if not self.grades:
return 0
return sum(self.grades) / len(self.grades)

Использование:

s = Student("Анна", 20)
s.add_grade(5)
s.add_grade(4)
print(s.average_grade()) # 4.5

4. Магия self и почему он нужен

self — это первый параметр любого метода. Когда вы пишете s.add_grade(5), Python автоматически передаёт s как self. Не называйте его по-другому (хотя технически можно), это общепринятое имя.

5. Атрибуты класса vs атрибуты объекта

Иногда нужно, чтобы все объекты делили одно значение. Например, название школы. Для этого используют атрибуты класса:

class Student:
school = "Школа №1" # атрибут класса

def __init__(self, name):
self.name = name # атрибут объекта

print(Student.school) # Школа №1
s = Student("Анна")
print(s.school) # Школа №1
s.school = "Школа №2" # создаст атрибут объекта, не трогая класс
print(Student.school) # всё ещё Школа №1

6. Инкапсуляция (скрытие данных)

В Python нет строгих приватных атрибутов, но есть соглашение: если имя начинается с подчёркивания _ — не трогайте его извне. Двойное подчёркивание __ включает механизм name mangling (усложняет доступ).

class BankAccount:
def __init__(self, balance):
self._balance = balance # защищённый (не лезь без нужды)

def deposit(self, amount):
self._balance += amount

def get_balance(self):
return self._balance

7. Типичные ошибки новичков

❌ Забыли self в параметрах метода

def say_hello(): # нет self
print(f"Привет, {self.name}") # NameError

❌ Пытаемся вызвать метод без создания объекта

Student.say_hello() # TypeError: missing required argument 'self'

❌ Путаем атрибуты класса и объекта

Изменение атрибута класса через объект создаёт новый атрибут объекта, а не меняет класс.

❌ Не используем __init__ для инициализации

Лучше задать все атрибуты в конструкторе, а не добавлять их «на лету» после создания.

8. Живой пример: телефонная книга в стиле ООП

Перепишем нашу телефонную книгу с классами:

class Contact:
def __init__(self, name, phone):
self.name = name
self.phone = phone

def __str__(self):
return f"{self.name} — {self.phone}"

class PhoneBook:
def __init__(self):
self.contacts = []

def add(self, name, phone):
self.contacts.append(Contact(name, phone))

def find(self, name):
found = [c for c in self.contacts if c.name.lower() == name.lower()]
return found

def show_all(self):
for c in self.contacts:
print(c)

def save_to_file(self, filename):
import json
data = [{"name": c.name, "phone": c.phone} for c in self.contacts]
with open(filename, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)

def load_from_file(self, filename):
import json
import os
if not os.path.exists(filename):
return
with open(filename, "r", encoding="utf-8") as f:
data = json.load(f)
self.contacts = [Contact(d["name"], d["phone"]) for d in data]

# Использование
pb = PhoneBook()
pb.load_from_file("contacts.json")
pb.add("Анна", "123-456")
pb.show_all()
pb.save_to_file("contacts.json")

Теперь код стал намного понятнее: PhoneBook управляет списком контактов, Contact представляет один контакт.

Заключение

Классы — это не магия, а удобный способ группировать данные и функции. Вы узнали:

  • как объявить класс и создать объект,
  • зачем нужен __init__ и self,
  • разницу между атрибутами объекта и класса,
  • основы инкапсуляции.

Следующая статья будет про наследование — когда один класс берёт свойства другого, позволяя переиспользовать код ещё эффективнее. А пока — попробуйте описать классом что-то из реального мира: книгу, автомобиль, рецепт. Это лучший способ привыкнуть к ООП.

Делитесь в комментариях, какой класс вы написали первым и что в нём хранили.

Статья подготовлена для канала «Код как искусство». Подписывайтесь, чтобы писать код, который думает об объектах.