1

我想定义一个执行以下操作的类:

Class computer():
    def __init__(self, x):
        # compute first the 'helper' properties
        self.prop1 = self.compute_prop1(x)
        self.prop2 = self.compute_prop2(x)
        # then compute the property that depends on 'helpers'
        self.prop3 = self.compute_prop3(x)

    def compute_prop1(self, x):
        return x
    def compute_prop2(self, x):
        return x*x
    def compute_prop3(self, x):
        return self.prop1 + self.prop2

然后,当我初始化一个实例时,我会按顺序计算所有属性(第一个助手,然后一切都取决于助手):

>>> computer = Computer(3)
>>> computer.__dict__
{'prop1': 3, 'prop2': 9, 'prop3': 12}

但是,我认为编写此代码有更好的做法,例如使用装饰器。你能给我一些提示吗?谢谢!

4

2 回答 2

3

这是您使用属性的类(添加了返回每个属性的方法):

Class PropertyComputer:
    def __init__(self, x):
        self._x = x

    @property
    def prop1(self):
        return self._x

    @property
    def prop2(self):
        return self._x * self._x

    @property
    def prop3(self):
        return self.prop1 + self.prop2

    def get_props(self):
        return self.prop1, self.prop2, self.prop3

在设计方面,我相信这会更好,因为:

  • 存储x为实例变量更有意义:使用对象的目的是避免传递变量,尤其是那些对象本身可以跟踪的变量;
  • 属性分配及其相应的计算在每个属性装饰方法中捆绑在一起:我们永远不必考虑问题出在 init 方法(定义属性的地方)还是计算方法(其中的逻辑属性的计算已列出)。

请注意,“首先计算帮助器,然后是依赖于它们的属性”的概念并不真正适用于此代码:我们只需要评估prop3是否/何时需要它。如果我们从不访问它,我们就永远不需要计算它。

与您的示例相比,使用属性的“坏”副作用是这些属性没有“存储”在任何地方(因此我添加了最后一种方法):

c = PropertyComputer(x=2)
c.__dict__  # outputs {'_x': 2}

另请注意,使用装饰器时,属性会在您访问它们时即时计算,而不是在 init 方法中只计算一次。以这种方式,属性修饰的方法像方法一样工作,但像属性一样被访问(这是使用它们的全部意义):

c = PropertyComputer(x=2)
c.prop1  # outputs 2
c._x = 10
c.prop1  # outputs 10

作为旁注,您可以使用functools.cached_property缓存其中一个属性的评估,以防计算成本高昂。

于 2020-02-28T16:15:56.200 回答
0

我认为以下是避免冗余的最简单方法

class computer():
    def __init__(self, x):
        self.prop_dict = self.compute_prop_dict(x)

    def compute_prop_dict(self, x):
        prop1 = x
        prop2 = x*x
        return {'prop1': prop1, 'prop2': prop2, 'prop3': prop1 + prop2}

所以实例化之后的任何东西都可以通过prop_dict

但正如 Brian 作为评论所说,这个命令只是 Python 3.7 的语言规范

于 2020-02-28T16:06:08.923 回答