根据给定的参数设置依赖属性

时间:2017-02-05 20:38:08

标签: python properties

我有一个具有3个属性的类,它们依赖于以下等式:c = a * b。如果给出任何2个属性,它应该能够设置所有3.

到目前为止,我想出了下面的解决方案,但我确信它可以更好地解决。有多种解决方案可以显示如何以一种方式定义依赖属性(如here),但我不知道如何将其扩展到我的案例,并且能够有效地设置任何对(a,b)或b,c或a,c),并计算最后一个。我是否需要查看HasTrait之类的prop(**kwargs)

我的想法下面有一个prop(_a = a, _b = b)函数,它总是设置给定任何2个值的所有值。因此,应该被称为以下三种方式之一:prop(_b = b, _c = c)prop(_a = a, _c = c)a bcprop()任何数字。

理想情况下,我希望使用密钥,实际属性的名称abc而不是{{1}来呼叫_a },_b_c。在我的下面的实现中,它不能用作abc调用prop()的设置者  再次导致无限循环。

class Example:
    # a * b = c

    def __init__(self, **kwargs):
        if len(kwargs) == 2: # need to specify at least 2 properties
            self.prop(**kwargs)
        else:
            self._a = self._b = self._c = None
            print('Correctly set properties manually using prop function')

    def prop(self, **kwargs):
        # needs to always set 2 properties out of a, b, c calculate the last one
        for key, value in kwargs.items():
            setattr(self, key, value)
        if '_a' not in kwargs:
            self._a = self.c / self.b
        elif '_b' not in kwargs:
            self._b = self.c / self.a
        elif '_c' not in kwargs:
            self._c = self.b * self.a

    @property
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self.prop(_a = value, _b = self.c / value) # set a holding c constant

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

    @b.setter
    def b(self, value):
        self.prop(_b = value, _a = self.c / value) # set b holding c constant

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

    @c.setter
    def c(self, value):
        self.prop(_c = value, _a = value / self.b) # set c holding b constant

1 个答案:

答案 0 :(得分:0)

我尝试了你的代码,但它没有用。结果:

$ python test.py 
Traceback (most recent call last):   
  File "test.py", line 38, in <module>
    ex.a = 3   
  File "test.py", line 19, in a
    self.prop(_a = value, _b = self.c / value) # set a holding c constant   
  File "test.py", line 31, in c
    return self._c 
AttributeError: 'Example' object has no attribute '_c'

让我们再试一次。首先,设置_mra的相同类名,docblock和构造函数以及调用者传入的任何值。

class Example:
    """Define a class with three attributes, a, b, and c, such that
    c = a * b defines the relationship among them.

    In the event of a conflict, the two most recently set attributes
    determine the value of the third.
    """

    def __init__(self, **kwargs):
        self._a = self._b = self._c = None
        self._mra = []
        if 'a' in kwargs:
            self.a = kwargs['a']
        if 'b' in kwargs:
            self.b = kwargs['b']
        if 'c' in kwargs:
            self.c = kwargs['c']

用于检查动作的打印输出方法:

    def __repr__(self):
        return "Example(_a={self._a!r}, _b={self._b!r}, _c={self._c!r})".format(self=self)

接下来,一堆虚拟的getter属性。出于某种原因,@property必须与def分开,或者我们将它们放在一起。

    @property
    def a(self): return self._a
    @property
    def b(self): return self._b
    @property
    def c(self): return self._c

现在,setter属性。我们不是复制和粘贴代码,而是将所有内容都传递到单个_update_values方法中。

    @a.setter
    def a(self, value): self._update_values(_a=value)
    @b.setter
    def b(self, value): self._update_values(_b=value)
    @c.setter
    def c(self, value): self._update_values(_c=value)

现在,对于事情的肉。我假设可以将属性设置为值,或设置为值None。这是属性的默认值,因此不难理解。但是,当您尝试进行涉及None的数学运算时,会引发异常!所以必须围绕数学进行尝试。

这个想法是self._mra(&#34;最近的属性&#34;的缩写)是最近设置的属性名称的队列。如果设置属性,它将移动到队列的后面。更新对象时,最近更新的两个属性用于确定第三个属性。 因此,如果我设置A,B,C,则设置A,B时设置C,并且当设置C时使用B计算A,因为B,C是最近设置的属性。它是一个随意的规则,但是一致。

    def _update_values(self, **kwargs):
        for k,v in kwargs.items():
            try: self._mra.remove(k)
            except ValueError: pass
            self._mra.append(k)
            setattr(self, k, v)

        if len(self._mra) <= 1:
            return

        last2 = ''.join(self._mra[-2:])
        try:
            if last2 == '_a_b' or last2 == '_b_a':
                self._c = self._a * self._b
            elif last2 == '_a_c' or last2 == '_c_a':
                self._b = self._c / self._a
            elif last2 == '_b_c':
                self._a = self._c / self._b
            else:
                raise Exception("I didn't see THIS coming! Last2 = %s" % last2)
        except TypeError:
            # Something was set to None. No math for you!
            pass

最后,一些测试代码。请注意,我使用python3,并且除法运算倾向于使所有内容都成为浮点数。您可以使用//分区来解决这个问题,或者......不是。

ex = Example()
print(repr(ex))       # Example(_a=None, _b=None, _c=None)
ex.a = 3
print(repr(ex))       # Example(_a=3, _b=None, _c=None)
ex.b = 5
print(repr(ex))       # Example(_a=3, _b=5, _c=15)
print('c = ', ex.c)   # c =  15
ex.c = 40
print(repr(ex))       # Example(_a=8.0, _b=5, _c=40)
print('a = ', ex.a)   # a =  8.0

输出:

$ python test.py
Example(_a=None, _b=None, _c=None)
Example(_a=3, _b=None, _c=None)
Example(_a=3, _b=5, _c=15)
c =  15
Example(_a=8.0, _b=5, _c=40)
a =  8.0