Геттер с побочным эффектом
Я создаю класс, объекты которого инициализируются
куча XML-кода. Класс имеет возможность извлекать различные параметры из этого XML и кэшировать их внутри переменных состояния объекта. Потенциальная величина этих параметров велика и, скорее всего, пользователю не понадобится большинство из них. Вот почему я решил выполнить "ленивую" инициализацию.
В следующем тестовом примере такой параметр title
. Когда пользователь пытается получить доступ к нему в первый раз, функция getter анализирует XML, правильно инициализирует переменную состояния и возвращает ее значение:
class MyClass(object):
def __init__(self, xml=None):
self.xml = xml
self.title = None
def get_title(self):
if self.__title is None:
self.__title = self.__title_from_xml()
return self.__title
def set_title(self, value):
self.__title = value
title = property(get_title, set_title, None, "Citation title")
def __title_from_xml(self):
#parse the XML and return the title
return title
Это выглядит хорошо и отлично работает для меня. Однако меня немного беспокоит тот факт, что функция геттера фактически является "сеттерной" в том смысле, что она оказывает очень существенное побочное воздействие на объект. Является ли это законной проблемой? Если да, то как мне его адресовать?
Ответы
Ответ 1
В то время как геттер, безусловно, выполняет побочный эффект, что традиционно не считается плохим побочным эффектом. Поскольку getter всегда возвращает одно и то же (запрет любых промежуточных изменений состояния), он не имеет видимых пользователем побочных эффектов. Это типичное использование свойств, поэтому нечего беспокоиться.
Ответ 2
Этот шаблон дизайна называется Lazy initialization и имеет законное использование.
Ответ 3
Несколько лет спустя, но хорошо: в то время как ленивая инициализация в порядке сама по себе, я бы определенно не откладывал разбор xml и т.д., пока кто-то не обратится к объекту title
. Вычисляемые атрибуты должны вести себя как простые атрибуты, а простой доступ к атрибутам никогда не будет повышаться (при условии, что атрибут существует, конечно).
FWIW У меня был очень похожий случай в каком-то проекте, который я взял на себя, с ошибками разбора xml, происходящими в самых неожиданных местах, из-за того, что предыдущий разработчик использовал свойства так же, как и в примере OP, и ему пришлось исправить это путем размещения парсинга и валидации в момент инициализации.
Итак, используйте свойства для ленивой инициализации только тогда, когда и когда вы знаете, что первый доступ никогда не будет повышаться. Собственно, никогда не используйте свойство для чего-либо, что может поднять (по крайней мере, когда настройка получения - это другая ситуация). Else, не используйте свойство, сделайте getter явным методом и четко документируйте, что он может поднять то или это.
NB: использование свойства для кеширования не является проблемой здесь, это само по себе прекрасно.