根据其他属性计算的Pythonic类实例属性

问题描述 投票:0回答:3

我有一个类实例,其属性是根据其他属性计算得出的。这些属性将在实例的整个生命周期中发生变化。所有属性不一定是在对象初始化时定义的。

从其他属性计算属性的Pythonic方法是什么?

这是一个简单的例子,计算有许多输入变量(下面的“a”)和计算(“b”和“c”)。

a = something
b = function of a (a+5)
c = function of a and b (a*b)

我已经尝试了很多实现。这是一个很好的表达我意图的方式。

 class CalcAttr(object):

    def __init__(self):
        self._a = None
        self._b = None
        self._c = None

    @property
    def a(self):
        return self._a
    @a.setter
    def a(self,value):
        self._a = value

    @property
    def b(self):
        self.calc_b()
        return self._b

    @property
    def c(self):
        self.calc_c()
        return self._c


    def calc_b(self):
        self._b = self._a + 5

    def calc_c(self):
        self._c = self._a * self._b

def test():
    abc = CalcAttr()
    a = 5
    return abc.c

注意:如果我先调用 t.b,则 t.c 可以工作。

> >>> t=abc.test()
> >>> t.c Traceback (most recent call last):   File "<stdin>", line 1, in <module>   File "abc.py", line 22, in c
>     self.calc_c()   File "abc.py", line 29, in calc_c
>     self._c = int(self._a) * int(self._b) TypeError: int() argument must be a string or a number, not 'NoneType'
> >>> t.b 10
> >>> t.c 50
> >>>

请记住,大多数实际计算都依赖于多个属性(5-10 个输入变量和尽可能多的计算变量)。

我的下一次迭代将包括一个“calculate_model”函数,该函数将在检查所有输入均已定义后填充所有计算属性。也许这将是 pyhonic 答案?

谢谢!

更新 - 工作解决方案

我创建了一个按顺序计算每个属性的方法:

def calc_model(self):
    self.calc_b()
    self.calc_c()

每个计算属性都会调用该方法

@property
def c(self):
    self.calc_model()
    return self._c

我不确定这是否正确,但它按预期工作......

python class attributes
3个回答
29
投票

如果我正确理解你的问题,你应该在它们的吸气剂中计算

b
c
。您可能还应该要求用户在初始化程序中传递
a
的值,因为没有
b
就无法计算
c
a
。另外,似乎没有太多理由保留
_a
_b
_c
——除非
b
c
的计算成本很高并且您想要缓存他们。

例如:

class CalcAttr(object):

    def __init__(self, a):
        self.a = a

    @property
    def b(self):
        return self.a + 5

    @property
    def c(self):
        return self.a * self.b

这样

>>> x = CalcAttr(42)
>>> x.c
1974

0
投票

我理解@jme在接受的答案中建议的内容更优雅,但我仍然尝试修复原始示例并使其正常工作。这是代码。

class CalcAttr(object):

   def __init__(self):
       self._a = None
       self._b = None
       self._c = None

   @property
   def a(self):
       return self._a
   @a.setter
   def a(self,value):
       self._a = value   

   @property
   def b(self):
       self.calc_b()
       return self._b

   @property
   def c(self):
       self.calc_c()
       return self._c

   def calc_b(self):
       self._b = self._a + 5
    
   def calc_c(self):
       self._c = self.a * self.b

def test():
    abc = CalcAttr()
    abc.a = 5
    return abc.c

test()

代码将起作用,

50
是结果值。


0
投票

我多次需要这个,所以我尝试为其创建一个解决方案。 @compulated_property 可以像这样使用:

class CalcAttr(object, has_computed_properties):
    a: int

    @computed_property
    def b(self):
        return self.a + 5

    @computed_property
    def c(self):
        return self.a * self.b


# test

class TestComputedProperty(unittest.TestCase):
    def test_so_issue(self):
        so = CalcAttr()
        so.a = 5
        self.assertEqual(so.b, 10)
        self.assertEqual(so.c, 50)
$ python3 computed_tests.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
© www.soinside.com 2019 - 2024. All rights reserved.