Геттер с побочным эффектом

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

В следующем тестовом примере таким параметром является title. Когда пользователь пытается получить к нему доступ в первый раз, функция получения анализирует 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         

Это выглядит красиво и отлично работает для меня. Однако меня немного беспокоит тот факт, что функция-получатель на самом деле является "сеттером" в том смысле, что она имеет очень значительный побочный эффект на объект. Это законное беспокойство? Если да, то как я должен решить эту проблему?


person Boris Gorelik    schedule 19.01.2011    source источник
comment
Независимо от того, каков фактический ответ на вопрос, вы не должны использовать ведущие двойные подчеркивания. Они начинают коверкать имена, то есть много потенциальной боли и нулевой выигрыш. Просто используйте одно ведущее подчеркивание.   -  person    schedule 19.01.2011
comment
Я не понимаю, почему это проблема.   -  person The Communist Duck    schedule 19.01.2011
comment
Небольшое предложение по рефакторингу: не инициализировать self._title в конструкторе и заменить условие в геттере на not hasattr(self, "_title").   -  person Sven Marnach    schedule 19.01.2011
comment
@delnan: именно так PyDev Eclipse создает свойства по умолчанию.   -  person Boris Gorelik    schedule 19.01.2011
comment
@Sven: спасибо за предложение. Не хотите ли вы уточнить, почему это лучшая идея? Отдельный ответ будет идеальным, поэтому я могу проголосовать за него.   -  person Boris Gorelik    schedule 19.01.2011
comment
@Sven Marnach: not hasattr(self, '_title') имхо довольно необычно для ленивой инициализации, а также будет медленнее, чем обычный поиск атрибутов и проверка на None.   -  person lunaryorn    schedule 19.01.2011


Ответы (3)


Хотя геттер, безусловно, создает побочный эффект, традиционно это не считается плохим побочным эффектом. Поскольку геттер всегда возвращает одно и то же (за исключением любых промежуточных изменений состояния), он не имеет видимых пользователю побочных эффектов. Это типичное использование свойств, так что не о чем беспокоиться.

person Gabe    schedule 19.01.2011
comment
Прошу не согласиться: в этом случае синтаксический анализ xml может вызвать исключения, и никто не ожидает, что доступ к атрибуту вызовет какое-то исключение синтаксического анализа xml. У меня был очень похожий случай в проекте, который я взял на себя и переписал эту часть кода, чтобы синтаксический анализ происходил при создании экземпляра, чтобы на более позднем этапе не возникало исключений, если xml каким-либо образом поврежден. Свойство get НЕ должно вызывать никаких исключений. Ожидаете ли вы, что простой доступ к атрибуту что-то поднимет (конечно, при условии, что атрибут существует)? Вычисляемый атрибут должен быть таким же безопасным, как и обычный. - person bruno desthuilliers; 24.11.2017

Этот шаблон проектирования называется Отложенная инициализация, и его можно использовать по закону.

person Axarydax    schedule 19.01.2011
comment
см. мой комментарий к принятому ответу - ленивая инициализация в порядке, но это не означает, что доступ к свойствам должен разрешать что-либо поднимать. Если ваш класс использует ленивую инициализацию, либо убедитесь, что это никогда не будет подразумевать никаких исключений, либо не заставляйте пользователя думать, что он выполняет простой безопасный доступ к атрибуту, и сделайте ваш геттер явным методом, документируя тот факт, что он может вызвать это или то исключение. - person bruno desthuilliers; 24.11.2017

Спустя несколько лет, но хорошо: хотя ленивая инициализация сама по себе хороша, я бы определенно не откладывал синтаксический анализ xml и т. д., пока кто-то не получит доступ к объекту title. Предполагается, что вычисляемые атрибуты ведут себя как обычные атрибуты, а доступ к обычному атрибуту никогда не будет возникать (конечно, при условии, что атрибут существует).

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

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

NB: использование свойства для кэширования чего-либо здесь не проблема, это само по себе нормально.

person bruno desthuilliers    schedule 24.11.2017