Зарегистрируйтесь, чтобы продолжить обучение

Изменяемая конфигурация Python: Объектно-ориентированный дизайн

Многие объекты в ООП не являются абстракцией данных, а используются как способ сохранить конфигурацию для выполнения повторяющихся действий, таких как генерация HTML из Markdown или определение города по IP. Конфигурация осуществляется через передачу опций в конструктор объекта, а сами опции хранятся внутри и используются для всех последующих вызовов.

# Инициализируем объект с заданным таймаутом
request = Request(timeout=1)
response = request.get("<http://example.com>")

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

Создание нового объекта

Мы можем создать новый объект с нужной нам конфигурацией там, где это требуется:

# Инициализируем объект с новым заданным таймаутом
request = Request(timeout=10)
response = request.get("<http://example.com>")

Здесь мы создаем новый объект Request с новым параметром таймаута. Затем выполняется HTTP GET-запрос на http://example.com с использованием этого объекта.

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

Здесь речь идет про тот самый полиморфизм, о котором мы будем говорить в будущем.

Теперь рассмотрим следующий способ.

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

Следующий подход включает использование сеттеров, что позволяет изменять опции уже существующего объекта:

request = Request(timeout=1)
response = request.get("<http://example.com>")

# Изменение параметра таймаута
request.set_timeout(10)
response = request.get("<http://example.com>")

В данном случае мы начинаем с того же объекта Request, что и в предыдущем примере. Но теперь мы используем сеттер set_timeout(), чтобы изменить таймаут с одной до десяти секунд для этого объекта. Затем мы снова выполняем GET-запрос, и этот запрос теперь будет использовать новое значение таймаута.

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

Объект request используется совместно всеми частями системы. Это означает, что его изменение в одном месте повлияет на все последующие вызовы.

В реальной жизни такие вещи могут приводить к еще более серьезным последствиям. Допустим, мы работаем с классом MarkdownRenderer, который отвечает за рендеринг markdown в HTML. Этот класс имеет опцию sanitize, которая отвечает за включение или отключение безопасного рендеринга.

Если мы случайно отключим эту опцию, то теги <script>, вставленные в Markdown, отобразятся как есть. Это может быть допустимо для текста, который мы контролируем, например, для уроков на нашем сайте. Но это недопустимо для текста, введенного пользователями. Изменение опции sanitize в объекте MarkdownRenderer может создать уязвимость для межсайтового скриптинга (XSS).

Чтобы избежать этого, мы можем попытаться вернуть опцию обратно после того, как мы ее изменили:

md = MarkdownRenderer(sanitize=True)

# В одной части программы
html = md.render(markdown)

# В другой части программы отключаем санитайз
md.set_sanitize(False)
html = md.render(user_input)
md.set_sanitize(True)

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

Передача опций при каждом вызове

Лучшим решением данной проблемы может быть передача дополнительных параметров при каждом вызове метода:

request = Request(timeout=1)
response = request.get("<http://example.com>")

# Передаем дополнительный параметр при вызове метода
response = request.get("<http://example.com>", timeout=10)
response = request.get("<http://example.com>")  # таймаут остается равным 1

В данном случае во втором вызове метода get мы передаем дополнительный параметр timeout со значением 10. Это изменение затрагивает только данный вызов, а не весь объект request.

Таким образом, когда мы возвращаемся к третьему вызову метода get, первоначальное значение timeout (1) остается неизменным. Это предотвращает возможные проблемы, связанные с изменением состояния объекта. Еще это позволяет гибко управлять настройками для каждого вызова.

Выводы

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


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка веб-приложений на Django
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»