如果属性不存在则计算属性

时间:2016-02-11 00:39:18

标签: python

我正在尝试访问不应该在我班级的__init__方法中创建的属性,但可以通过调用其他方法来计算。我试图这样做,如果我尝试访问该属性,它不存在,它将自动计算。但是,如果属性确实存在,即使值不同,我也不希望重新计算它。例如:

class SampleObject(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def calculate_total(self):
        self.total = self.a + self.b

sample = SampleObject(1, 2)
print sample.total   # should print 3
sample.a = 2
print sample.total   # should print 3
sample.calculate_total()
print sample.total   # should print 4

到目前为止,我最好的解决方案是制作一个满足我需要的get_total()方法。

class SampleObject2(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def calculate_total(self):
        self.total = self.a + self.b

    def get_total(self):
        if hasattr(self, 'total'):
            return self.total
        else:
            self.calculate_total()
            return self.total

sample2 = SampleObject2(1, 2)
print sample2.get_total() # prints 3
sample2.a = 2
print sample2.get_total() # prints 3
sample2.calculate_total()
print sample2.get_total() # prints 4

这个工作正常,但是我已经知道在python中使用getter是不受欢迎的,我希望每次想要访问该属性时都避免调用此函数。这是我最好的解决方案,还是有更清洁,更pythonic的方式来做到这一点?

这是我编写的一个例子。在我的实际问题中,calculate_total()是一个耗时的过程,不一定需要被调用。所以我不想在 init 方法中执行它。

2 个答案:

答案 0 :(得分:9)

您想使用@property装饰器。创建一个方法,该方法将像普通属性一样进行,进行延迟计算:

class SampleObject:

    def __init__(self):
        # ...
        self._total = None

    @property
    def total(self):
        """Compute or return the _total attribute."""
        if self._total is None:
            self.compute_total()

        return self._total

答案 1 :(得分:2)

Pyramid(一个Web框架)附带一个类似于reify的{​​{1}}装饰器(由Austin Hastings展示)但它的工作方式略有不同:该函数只执行一次,之后,始终使用函数返回的值。它本质上是做奥斯汀的代码所做的,但不必使用单独的属性:它是该模式的概括。

您可能不希望仅为这一个装饰器使用整个Web框架,所以这是我写的等效文件:

property

用法:

import functools

class Descriptor(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, inst, type=None):
        val = self.func(inst)
        setattr(inst, self.func.__name__, val)
        return val

def reify(func):
    return functools.wraps(func)(Descriptor(func))