Добавить в корзинуПозвонить
Найти в Дзене
Технологии

Инкапсуляция и свойства в Python: защищаем данные в проекте ISBN-кодами

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

Приветствую вас на втором этапе нашего путешествия по миру объектно-ориентированного программирования (ООП) в Python! После знакомства с основами классов и объектов пришло время взглянуть на одну из важнейших особенностей ООП — инкапсуляцию и изучить её важнейший инструмент — свойства.

Кролик защищает морковку
Кролик защищает морковку

Инкапсуляция что это простыми словами

Инкапсуляция — это концепция, согласно которой внутренние данные объекта скрыты от внешнего мира, обеспечивая защиту от нежелательного вмешательства извне. Одним из лучших примеров инкапсуляции являются ситуации, когда нужно контролировать доступ к определенным атрибутам объекта.

Например, представьте, что наша книга имеет уникальный международный стандартный номер книги (ISBN) — специальный идентификационный код. Важно убедиться, что этот код соответствует стандарту и корректен. Если позволить внешнему миру свободно изменять этот атрибут, это может привести к ошибкам и проблемам в приложении.

Именно тут приходят на помощь приватные поля и свойства.

Приватные поля Python

В Python нельзя строго ограничить доступ к полям класса, однако существует соглашение, что имена, начинающиеся с символа подчёркивания `_`, считаются внутренними и использоваться извне не рекомендуется. Например, называя поле `_isbn`, мы сообщаем другим разработчикам, что оно должно быть доступно только через специальные методы.

Изменять такие поля напрямую считается плохим стилем, потому что иначе нарушается целостность данных.

Пример объявления приватного поля:

class Book:
def __init__(self, title, author, year, isbn):
self.title = title
self.author = author
self.year = year
self._isbn = isbn # Приватное поле начинается с "_"
Объявлениеприватного поля
Объявлениеприватного поля

Но как же теперь изменить или прочитать это поле, соблюдая правила инкапсуляции? Тут нам пригодятся свойства.

Свойства (properties) в Python

Свойство — это особый вид атрибута, который позволяет читать и устанавливать значение, контролируя доступ. Давайте посмотрим, как это реализуется на примере ISBN-кода.

Чтение и запись значений с проверкой

Сначала создадим два метода:

- `getter` — метод, который возвращает значение.

- `setter` — метод, который устанавливает новое значение, предварительно проверив его валидность.

Используя декораторы `@property` и `@isbn.setter`, мы объявляем соответствующее свойство и контролируем изменение данных:

class Book:
# ...
@property
def isbn(self):
"""Getter для чтения ISBN."""
return self._isbn
@isbn.setter
def isbn(self, value):
"""Setter для установки ISBN с проверкой правильности."""
if len(value) != 13 or not value.isdigit(): # Проверка длины и цифр
raise ValueError("Неверный ISBN!")
self._isbn = value
Чтение и запись значений с проверкой, доступ к ISBN возможен только через эти методы getter и setter
Чтение и запись значений с проверкой, доступ к ISBN возможен только через эти методы getter и setter

Теперь доступ к ISBN возможен только через эти методы, гарантирующие контроль над изменениями.

Почему это полезно?

Благодаря такому подходу мы добиваемся нескольких важных преимуществ:

- Контроль изменений: Устанавливаемые значения проходят строгую проверку, что предотвращает случайные ошибки и повышает надежность программы.

- Единственный источник истины: Все манипуляции с полем происходят централизованно, исключая дублирование проверок.

- Совместимость API: Внешний интерфейс остаётся неизменным даже при изменении внутренней реализации (это хорошо для совместимости версий и поддержки крупных проектов).

Полный рабочий пример класса Book с защитой ISBN

Ниже представлен обновлённый и дополненный вариант класса Book, который защищает поле ISBN с помощью свойств и демонстрирует применение инкапсуляции:

Конструктор класса Book принимает четыре параметра:
        title - название книги,
        author - автор книги,
        year - год выпуска,
        isbn - уникальный идентификатор книги (ISBN).
        
Значения присваиваются публичным полям (title, author, year), а ISBN сохраняется в приватном поле _isbn для защиты.
Конструктор класса Book принимает четыре параметра: title - название книги, author - автор книги, year - год выпуска, isbn - уникальный идентификатор книги (ISBN). Значения присваиваются публичным полям (title, author, year), а ISBN сохраняется в приватном поле _isbn для защиты.
Getter-метод isbn, предназначенный для безопасного чтения значения ISBN.  Вместо прямого доступа к _isbn используется getter, который обеспечивает чтение приватного поля через специальное свойство.  Setter-метод isbn предназначен для безопасной записи значения ISBN. Перед сохранением проверяется длина и содержимое ISBN.
Getter-метод isbn, предназначенный для безопасного чтения значения ISBN. Вместо прямого доступа к _isbn используется getter, который обеспечивает чтение приватного поля через специальное свойство. Setter-метод isbn предназначен для безопасной записи значения ISBN. Перед сохранением проверяется длина и содержимое ISBN.
Создаём экземпляр книги с правильным ISBN. Читаем ISBN через getter. Попробуем поменять ISBN на неправильный (будет ошибка!). Ещё один пример правильной замены ISBN
Создаём экземпляр книги с правильным ISBN. Читаем ISBN через getter. Попробуем поменять ISBN на неправильный (будет ошибка!). Ещё один пример правильной замены ISBN

Результат работы программы:

Название: Путешествие Гулливера
Автор: Джонатан Свифт
Год: 1726
ISBN: 9781234567890
Неверный ISBN! Длина должна быть ровно 13 символов и содержать только цифры.
Новый ISBN: 9780987654321
Название: Путешествие Гулливера Автор: Джонатан Свифт Год: 1726 ISBN: 9781234567890 Неверный ISBN! Длина должна быть ровно 13 символов и содержать только цифры. Новый ISBN: 9780987654321

Что происходит под капотом в нашей программе

Разберём каждое важное место подробно:

1. Конструктор (__init__)

Конструктор (__init__) с вызовом setter через self.isbn = isbn
Конструктор (__init__) с вызовом setter через self.isbn = isbn

Почему именно self.isbn = isbn вызывает setter? Потому что Python перехватывает любое обращение к атрибуту, которое помечено специальным декоратором @property. Хотя кажется, будто мы обращаемся к обычной переменной, на самом деле выполняется метод-установщик (setter).

2. Свойство-getter (@property)

Данный метод getter — это средство для считывания значения приватного поля _isbn.
Данный метод getter — это средство для считывания значения приватного поля _isbn.

Данный метод — это простое средство для считывания значения приватного поля _isbn. Его задача — вернуть правильное значение без прямой передачи ссылок наружу. Таким образом, мы скрываем внутреннюю реализацию и делаем возможным дальнейшее расширение (например, можем добавить логику аудита или журналирования при каждом чтении).

3. Свойство-setter (@isbn.setter)

Setter-метод isbn предназначен для безопасной записи значения ISBN с полной проверкой введённого значения ISBN перед тем, как оно попадёт в внутреннее хранилище (_isbn). Перед сохранением проверяется длина и содержимое ISBN.
Setter-метод isbn предназначен для безопасной записи значения ISBN с полной проверкой введённого значения ISBN перед тем, как оно попадёт в внутреннее хранилище (_isbn). Перед сохранением проверяется длина и содержимое ISBN.

Метод-записчик (setter) — это критически важная часть защиты данных. Здесь осуществляется полная проверка введённого значения ISBN перед тем, как оно попадёт в внутреннее хранилище (_isbn). Если данные не соответствуют требованиям (например, длина отличается от 13 символов или там содержатся буквы), возникает ошибка ValueError, и присваивание прерывается.

4. Безопасность и преимущества инкапсуляции

Использование свойств позволяет достичь следующего:

Защита данных: Нельзя случайно записать неправильное значение, так как вся логика контроля сосредоточена в одном месте — setters.
Безопасность интерфейса: Пользователь не сможет непосредственно обратиться к приватному полю _isbn, поэтому невозможно повредить данные изнутри.
Расширяемость: Логика может быть усложнена без влияния на внешний интерфейс. Например, позже можно ввести проверку по онлайн-сервисам, криптографическую подпись или дополнительную обработку.

Работа геттера и сеттера (чтение и запись значений через методы)

Логика работы геттера

Когда мы пытаемся считать значение какого-то свойства, фактически вызывается соответствующий метод-геттер. То есть любая попытка прочитать значение приводит к выполнению заранее подготовленного метода.

Шаги при обращении к геттеру:

Код обращается к атрибуту, например, так: obj.isbn.
Python распознаёт, что это атрибут, имеющий декорированное свойство
@property. Выполняется соответствующий метод, указанный в декораторе @property. Метод возвращает значение внутреннего приватного поля (или другое необходимое значение).
Пример вызова геттера:

print(book.isbn) # Вызывает метод isbn() и выводит ISBN

Под капотом происходит следующее:

# Вместо прямого доступа к isbn реально вызывается следующий метод:
value = obj.isbn() # Внутренний вызов метода isbn()

Логика работы сеттера

Процесс аналогичен чтению, но направлен на установку новых значений. Всякий раз, когда мы хотим сохранить новое значение в поле, сначала вызывается метод-сеттер, который контролирует правильность входящего значения.

Шаги при установке значения через сеттер:

Выражение пытается установить значение, например: obj.isbn = new_value.
Python видит, что атрибут защищён с помощью декоратора
@*.setter.
Происходит вызов соответствующего метода-сеттера, указанного в декораторе.
В методе-сеттере выполняются необходимые проверки и установка значения в приватное поле.
Пример вызова сеттера:

book.isbn = "9781234567890" # Установка нового значения ISBN

Под капотом происходит следующее:

# Вместо прямой установки выполняется вызов метода:
obj.isbn(new_value) # вызывается метод isbn(value)

Подведем итоги процесса работы геттеров/сеттеров

Просто запомните правило:

  • Любое чтение атрибутов (через точку) ведёт к вызову метода-геттера.
  • Любая запись (при помощи оператора = слева от точки) приводит к вызову метода-сеттера.

То есть Python незаметно заменяет прямое обращение к атрибутам этими вызовами, обеспечивая полную прозрачность и сокрытие внутренних деталей реализации.Такова магия свойств (@property и @*.setter) — она делает ваш код одновременно удобным и безопасным. Пользуйтесь ей активно и смело, ведь она действительно улучшает дизайн и снижает вероятность появления ошибок в программе.

🎓 Практическое задание

Попробуйте сами дополнить класс новыми свойствами или дополнительными условиями. Например, добавьте проверку на минимально допустимый год издания книги (предложите свой разумный порог) или дополнительные требования к названию и автору (например, минимальная длина текста).

Продолжим путешествие в следующей статье, где обсудим наследование и полиморфизм — две главные силы, позволяющие повторно использовать код и увеличивать гибкость вашей программы. До скорой встречи!

Наши шаги