【发布时间】:2015-04-20 16:49:47
【问题描述】:
举个简单的例子,以classEllipse为例,它可以返回其属性,例如面积A、周长C、长轴/短轴a/b、偏心率e等。为了实现这一点,显然必须提供精确的两个参数才能获得所有其他参数,尽管作为一种特殊情况,只提供一个参数应该假设一个圆形。三个或更多一致的参数应该会产生警告但有效,否则显然会引发异常。
所以一些有效Ellipses 的例子是:
Ellipse(a=5, b=2)
Ellipse(A=3)
Ellipse(a=3, e=.1)
Ellipse(a=3, b=3, A=9*math.pi) # note the consistency
无效的会是
Ellipse()
Ellipse(a=3, b=3, A=7)
因此构造函数将包含许多=None 参数,
class Ellipse(object):
def __init__(self, a=None, b=None, A=None, C=None, ...):
或者,可能更明智,一个简单的**kwargs,也许添加提供a,b作为位置参数的选项,
class Ellipse(object):
def __init__(self, a=None, b=None, **kwargs):
kwargs.update({key: value
for key, value in (('a', a), ('b', b))
if value is not None})
到目前为止,一切都很好。但现在是实际的实现,即确定提供了哪些参数,哪些没有提供,并根据它们确定所有其他参数,或者在需要时检查一致性。
我的第一种方法是简单而乏味的多种方法的组合
if 'a' in kwargs:
a = kwargs['a']
if 'b' in kwargs:
b = kwargs['b']
A = kwargs['A'] = math.pi * a * b
f = kwargs['f'] = math.sqrt(a**2 - b**2)
...
elif 'f' in kwargs:
f = kwargs['f']
b = kwargs['b'] = math.sqrt(a**2 + f**2)
A = kwargs['A'] = math.pi * a * b
...
elif ...
等等*。但是没有更好的办法吗?还是这个类的设计完全是胡扯,我应该创建诸如Ellipse.create_from_a_b(a, b) 之类的构造函数,尽管这基本上使“提供三个或更多一致的参数”选项变得不可能?
奖励问题:由于ellipse's circumference 涉及椭圆积分(或椭圆函数,如果提供周长并且要获得其他参数),这些计算在计算上并不完全是微不足道的,如果这些计算实际上是在构造函数中,或者更确切地说是放入@property Ellipse.C?
* 我猜至少一个可读性改进总是提取a 和b 并从中计算其余部分,但这意味着重新计算已经提供的值,浪费时间和精度。 ..
【问题讨论】:
-
严格来说,3个参数即使是一致的,也应该被认为是过度定义的。
-
@figs True... 我想还有更明智的例子,例如线性方程组可以被过度定义,但有一个独特的解决方案
-
我会避免允许两个以上的参数。这需要您检查一致性,由于数值精度问题,这实际上很难在一般意义上正确执行。
-
我们可以假设这个
Ellipse对象是不可变的吗?这简化了实现,因为改变现有Ellipse对象的a是不明确的——它会影响其他值,但很难猜测例如b保持不变还是A保持不变。我问是因为如果我们同意使用属性,那么我们需要考虑到它。对于“我猜至少有一个可读性改进是......浪费时间和精度” - 如果不计算和比较所有内容,就无法在过度定义的场景中保持一致性。 -
@TobiasKienzler:是的,但问题仍然存在,任何人想要提供的输入变量的 n-choose-k 可能组合中有多少?我要说的是,在大多数现实世界的环境中,你并不需要真正支持每一种可能的组合。你支持特定的组合,因为你有特定的原因。 (例如,如果您在围栏建筑环境中使用圆周,或者在地板涂漆环境中使用区域,您可能允许圆周作为输入,但允许某些输入的动机是代数关系之外的。)跨度>
标签: python arguments optional-parameters