带副作用的getter

16 投票
3 回答
1963 浏览
提问于 2025-04-16 10:15

我创建了一个类,这个类的对象是用一堆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         

这看起来不错,对我来说也能正常工作。不过,我有点担心的是,这个获取函数实际上在某种意义上是一个“设置器”,因为它对对象有很大的副作用。这算是一个合理的担忧吗?如果是的话,我该怎么解决这个问题呢?

3 个回答

2

虽然这段话是几年前写的,但我觉得还是有必要说一下:懒加载(lazy initialization)本身没问题,但我绝对不建议等到有人访问对象的 title 时再去解析 XML 等操作。计算属性应该像普通属性一样工作,而普通属性的访问是绝对不会出错的(当然前提是这个属性是存在的)。

顺便提一下,我在接手的一个项目中遇到过类似的情况,XML 解析错误出现在最意想不到的地方,因为之前的开发者像 OP 示例中那样使用属性,结果我不得不在实例化的时候就处理解析和验证的部分。

所以,只有在你确定第一次访问绝对不会出错的情况下,才可以使用属性进行懒加载。实际上,任何可能会出错的情况都不要用属性(至少在获取时是这样,设置是另一回事)。否则,就不要用属性,直接把获取方法写成一个明确的方法,并清楚地说明可能会出错。

注意:用属性来缓存某些东西本身并没有问题,这样做是可以的。

15

这个设计模式叫做懒加载,它是有实际用途的。

4

虽然这个获取器(getter)确实会产生一些副作用,但通常来说,这并不是我们认为的坏副作用。因为这个获取器每次返回的结果都是一样的(除非状态发生了变化),所以对用户来说没有任何可见的副作用。这是属性的典型用法,所以没有什么好担心的。

撰写回答