Класс, объект, атрибуты, методы, инкапсуляция на примере простого класса на Python
От процедурного программирования к объектному
В процедурном программировании, с которым мы работали до сих пор, данные (переменные, списки) и обрабатывающие их подпрограммы (функции) существуют отдельно друг от друга: функция принимает данные в качестве аргументов и возвращает результат, но никак не «привязана» к ним постоянно. Объектно-ориентированное программирование (ООП) — это подход, при котором данные и методы их обработки объединяются в единую сущность — объект. Такой подход упрощает моделирование реальных предметов и понятий (учеников, геометрических фигур, банковских счетов), каждый из которых обладает как характеристиками (данными), так и типичным для него поведением (действиями).
Класс и объект
Класс — это описание («чертёж», шаблон) будущих объектов: он определяет, какими данными (атрибутами) будет обладать объект этого класса и какие действия (методы) над ним можно выполнять. Объект (или экземпляр класса) — это конкретная реализация класса, создаваемая по его шаблону и обладающая собственными, независимыми от других объектов значениями атрибутов. Соотношение класса и объекта похоже на соотношение чертежа дома и конкретных построенных по этому чертежу домов: чертёж один, а домов, построенных по нему, может быть сколько угодно, и у каждого — свой адрес и свои жильцы.
class Dog:
pass # пока пустой класс — просто демонстрация синтаксиса
# Создание объектов (экземпляров) класса Dog
rex = Dog()
bobik = Dog()
print(type(rex)) # <class '__main__.Dog'>
print(rex is bobik) # False — это два разных объекта
Атрибуты объекта и конструктор __init__
Атрибуты — это переменные, хранящие данные (характеристики) конкретного объекта. Чтобы задать атрибуты при создании объекта, в классе определяют специальный метод __init__ — конструктор, который автоматически вызывается при создании нового объекта и получает значения для его атрибутов. Первый параметр любого метода класса, включая __init__, называется self и представляет собой ссылку на тот объект, для которого метод вызывается; через self внутри методов обращаются к атрибутам именно этого объекта.
class Student:
def __init__(self, name, grade):
self.name = name # атрибут — имя ученика
self.grade = grade # атрибут — класс обучения
self.scores = [] # атрибут — список оценок
# Создание объектов с разными значениями атрибутов
student1 = Student('Анна', 11)
student2 = Student('Пётр', 10)
print(student1.name, student1.grade) # Анна 11
print(student2.name, student2.grade) # Пётр 10
Методы класса
Методы — это функции, определённые внутри класса и описывающие поведение объекта — действия, которые он может выполнять или которые можно выполнить над ним. Метод, как и конструктор, обязательно принимает первым параметром self и через него получает доступ к атрибутам конкретного объекта, для которого метод был вызван.
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
self.scores = []
def add_score(self, score):
self.scores.append(score)
def average_score(self):
if not self.scores:
return 0
return sum(self.scores) / len(self.scores)
student1 = Student('Анна', 11)
student1.add_score(5)
student1.add_score(4)
student1.add_score(5)
print(student1.scores) # [5, 4, 5]
print(student1.average_score()) # 4.666…
Создание нескольких объектов одного класса
Главное преимущество класса — возможность создавать сколько угодно независимых объектов на его основе; изменение атрибутов одного объекта никак не влияет на атрибуты другого, даже если оба созданы из одного и того же класса.
students = [Student('Анна', 11), Student('Пётр', 11), Student('Мария', 11)]
students[0].add_score(5)
students[0].add_score(5)
students[1].add_score(3)
students[1].add_score(4)
students[2].add_score(5)
students[2].add_score(4)
for s in students:
print(f'{s.name}: средний балл {s.average_score():.2f}')
Инкапсуляция данных
Инкапсуляция — один из ключевых принципов ООП, заключающийся в объединении данных и методов их обработки внутри объекта и в ограничении прямого доступа извне к внутренним данным объекта, если это нежелательно. В Python нет строгого запрета доступа к атрибутам, как в некоторых других языках, но принято обозначать «внутренние», не предназначенные для прямого использования извне атрибуты одним подчёркиванием в начале имени (_balance) — как соглашение для программистов, а двумя подчёркиваниями (__balance) — для более сильной защиты (имя такого атрибута автоматически изменяется интерпретатором, что затрудняет случайное обращение к нему извне). Вместо прямого доступа к таким атрибутам создают специальные методы, которые контролируют, каким образом атрибут можно читать или изменять.
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.__balance = balance # 'приватный' атрибут
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print('Недостаточно средств или неверная сумма')
def get_balance(self):
return self.__balance
account = BankAccount('Иван', 1000)
account.deposit(500)
account.withdraw(2000) # 'Недостаточно средств или неверная сумма'
print(account.get_balance()) # 1500
Метод __str__ для удобного вывода объекта
По умолчанию при попытке напечатать объект (print(obj)) Python выводит малоинформативную строку вида <__main__.Student object at 0x…>. Чтобы задать понятное текстовое представление объекта, в классе определяют специальный метод __str__, который автоматически вызывается функцией print() и должен возвращать строку.
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __str__(self):
return f'Ученик {self.name}, {self.grade} класс'
student1 = Student('Анна', 11)
print(student1) # Ученик Анна, 11 класс
Атрибуты класса и атрибуты объекта
Помимо атрибутов конкретного объекта, задаваемых через self в конструкторе, в классе можно объявить атрибут класса — переменную, объявленную непосредственно внутри тела класса, но вне какого-либо метода. Такой атрибут является общим для всех объектов класса: если не переопределить его индивидуально для конкретного объекта, все объекты будут видеть одно и то же значение. Атрибуты класса удобны для хранения общих для всех объектов данных, например счётчика количества уже созданных объектов.
class Student:
school_name = 'Школа №1' # атрибут класса — общий для всех
total_students = 0 # счётчик созданных объектов
def __init__(self, name):
self.name = name # атрибут объекта — свой у каждого
Student.total_students += 1
s1 = Student('Анна')
s2 = Student('Пётр')
print(s1.school_name, s2.school_name) # оба видят одно и то же значение
print('Всего учеников создано:', Student.total_students) # 2
Полный пример: класс Rectangle
Рассмотрим итоговый пример класса «Прямоугольник», объединяющего атрибуты (ширину и высоту), методы для вычисления производных величин (площади и периметра), инкапсуляцию (проверку корректности значений при создании) и удобное текстовое представление.
class Rectangle:
def __init__(self, width, height):
if width <= 0 or height <= 0:
raise ValueError('Стороны должны быть положительными')
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def is_square(self):
return self.width == self.height
def __str__(self):
return f'Прямоугольник {self.width}x{self.height}'
rectangles = [Rectangle(4, 5), Rectangle(3, 3), Rectangle(7, 2)]
for r in rectangles:
print(f'{r}: площадь={r.area()}, периметр={r.perimeter()}, квадрат={r.is_square()}')
ООП и уже изученные структуры данных
Атрибутом объекта может быть не только число или строка, но и любая другая изученная структура данных — список, строка или даже другой объект. Это позволяет классу естественно объединять коллекцию данных с методами её обработки: например, класс «Группа» может хранить список объектов класса Student и предоставлять методы для вычисления среднего балла всей группы, используя ровно те же приёмы обхода массива и суммирования, которые уже были рассмотрены при изучении одномерных массивов.
class Group:
def __init__(self, students):
self.students = students # список объектов класса Student
def group_average(self):
total = 0
count = 0
for s in self.students:
total += s.average_score()
count += 1
return total / count if count else 0
group = Group([Student('Анна', 11), Student('Пётр', 11)])
group.students[0].add_score(5)
group.students[1].add_score(4)
print(f'Средний балл группы: {group.group_average():.2f}')
Типичные ошибки
Самая частая ошибка начинающих — забыть указать параметр self первым параметром метода, что приводит к ошибке TypeError при вызове метода (Python автоматически передаёт объект первым аргументом). Вторая ошибка — путать атрибут класса (общий для всех объектов, объявленный внутри класса, но вне __init__) с атрибутом объекта (индивидуальным, задаваемым через self в __init__): изменение изменяемого атрибута класса (например, списка) через один объект неожиданно затрагивает все объекты этого класса. Третья ошибка — обращение к атрибуту объекта до его создания в __init__ (например, использование self.total до строки self.total = 0), что вызывает AttributeError. Наконец, часто забывают, что вызов метода объекта происходит через точку: obj.method(), а не method(obj) или Class.method() без указания объекта.
Краткий словарь терминов
Класс — шаблон (описание), определяющий атрибуты и методы будущих объектов.
Объект (экземпляр класса) — конкретная реализация класса с собственными значениями атрибутов.
Атрибут — переменная, хранящая данные (характеристику) объекта.
Метод — функция, определённая внутри класса и описывающая поведение объекта.
self — первый параметр метода, ссылающийся на тот объект, для которого метод вызван.
Конструктор __init__ — специальный метод, автоматически вызываемый при создании объекта и задающий его начальные атрибуты.
Инкапсуляция — объединение данных и методов внутри объекта с ограничением прямого доступа к внутренним данным извне.
Заключение
Классы и объекты позволяют объединять данные и обрабатывающие их алгоритмы в единые, самостоятельные и легко переиспользуемые сущности, что делает объектно-ориентированный подход основой для построения больших и хорошо структурированных программ. Освоенные в этом разделе структуры данных, алгоритмы и принципы ООП образуют фундамент программирования, необходимый как для решения задач ЕГЭ по информатике, так и для дальнейшего изучения профессиональной разработки программного обеспечения.



