Getter със страничен ефект

Създавам клас, чиито обекти се инициализират с куп 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         

Това изглежда добре и работи добре за мен. Въпреки това съм обезпокоен малко от факта, че getter функцията всъщност е "setter" в смисъл, че има много значителен страничен ефект върху обекта. Това основателна загриженост ли е? Ако е така, как трябва да го реша?


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
cf моя коментар за приетия отговор - мързеливата инициализация е наред, но това не означава, че достъпът до собственост трябва да бъде разрешен за повдигане на нещо. Ако вашият клас използва мързелива инициализация, или се уверете, че това никога няма да доведе до някакво изключение, или не примамвайте потребителя да мисли, че прави обикновен безопасен достъп до атрибута, и направете вашия getter изричен метод, документирайки факта, че може да повдигне това или това изключение. - person bruno desthuilliers; 24.11.2017

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

FWIW Имах много подобен случай в някакъв проект, който поех, с грешки при анализиране на xml, случващи се на най-неочакваните места, поради това, че предишният разработчик използва свойства по същия начин, както в OP примера, и трябваше да го поправи, като постави част от анализиране и валидиране по време на инстанция.

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

NB: използването на свойство за кеширане на нещо не е проблемът тук, това само по себе си е добре.

person bruno desthuilliers    schedule 24.11.2017