带副作用的getter
我创建了一个类,这个类的对象是用一堆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 个回答
虽然这段话是几年前写的,但我觉得还是有必要说一下:懒加载(lazy initialization)本身没问题,但我绝对不建议等到有人访问对象的 title
时再去解析 XML 等操作。计算属性应该像普通属性一样工作,而普通属性的访问是绝对不会出错的(当然前提是这个属性是存在的)。
顺便提一下,我在接手的一个项目中遇到过类似的情况,XML 解析错误出现在最意想不到的地方,因为之前的开发者像 OP 示例中那样使用属性,结果我不得不在实例化的时候就处理解析和验证的部分。
所以,只有在你确定第一次访问绝对不会出错的情况下,才可以使用属性进行懒加载。实际上,任何可能会出错的情况都不要用属性(至少在获取时是这样,设置是另一回事)。否则,就不要用属性,直接把获取方法写成一个明确的方法,并清楚地说明可能会出错。
注意:用属性来缓存某些东西本身并没有问题,这样做是可以的。
这个设计模式叫做懒加载,它是有实际用途的。
虽然这个获取器(getter)确实会产生一些副作用,但通常来说,这并不是我们认为的坏副作用。因为这个获取器每次返回的结果都是一样的(除非状态发生了变化),所以对用户来说没有任何可见的副作用。这是属性的典型用法,所以没有什么好担心的。