我有一个具有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
b
,c
,prop()
任何数字。
理想情况下,我希望使用密钥,实际属性的名称a
,b
,c
而不是{{1}来呼叫_a
},_b
,_c
。在我的下面的实现中,它不能用作a
,b
,c
调用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
答案 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