我很抱歉,如果标题是神秘的,我想不出用一个句子来描述我的问题。我正在用python2.7构建一些代码,我将在下面描述。
最小工作实例
我的代码有一个Parameter
类,它实现了name
和value
等属性,类似于下面的内容。
class Parameter(object):
def __init__(self, name, value=None, error=None, dist=None, prior=None):
self.name = name
self._value = value # given value for parameter, this is going to be changed very often in an MCMC sampler
self.error = error # initial estimate of error for the parameter, will only be set once
self._dist = dist # a distribution for the parameter, will only be set once
self.prior = prior
@property
def value(self):
return self._value
@property
def dist(self):
return self._dist
该类还具有几个属性,如果给定分布,则返回Parameter.dist
的平均值、中值等。
我还有另一个类,例如ParameterSample
,它创建了一个包含不同Parameter
对象的填充。其中一些Parameter
对象具有使用Parameter.set_parameter()
函数设置的属性(例如value
、error
),但其他一些Parameter
对象没有显式设置,但它们的value
和dist
属性依赖于设置的其他Parameter
对象:
class ParameterSample(object):
def __init__(self):
varied_parameters = ('a', 'b') # parameter names whose `value` attribute is varied
derived_parameters = ('c',) # parameter names whose `value` attribute is varied, but depends on `a.value` and `b.value`
parameter_names = varied_parameters + derived_parameters
# create `Parameter` objects for each parameter name
for name in parameter_names:
setattr(self, name, Parameter(name))
def set_parameter(self, name, **kwargs):
for key, val in kwargs.items():
if key == 'value':
key = '_'.join(['', key]) # add underscore to set `Parameter._value`
setattr(getattr(self, name), key, val) # basically does e.g. `self.a.value = 1`
我现在可以创建一个ParameterSample
并像这样使用它们:
parobj = ParameterSample()
parobj.set_parameter('a', value=1, error=0.1)
parobj.set_parameter('b', value=2, error=0.5)
parobj.a.value
>>> 1
parobj.b.error
>>> 0.5
parobj.set_parameter('b', value=3)
parobj.b.value
>>> 3
parobj.b.error
>>> 0.5
我想要的
我最终想要的是,以同样的方式使用Parameter.c
。例如:
parobj.c.value
>>> 4 # returns parobj.a.value + parobj.b.value
parobj.c.dist
>>> None # returns a.dist + b.dist, but since they are not currently set it is None
因此,c
需要是一个Parameter
对象,具有与a
和b
相同的属性,但是它的value
和dist
根据a
和b
的当前属性进行更新。
但是,我还应该提到,我希望能够为参数c
设置允许的先验范围,例如,在调用其值之前设置parobj.set_parameter('c', prior=(0,10))
--因此在创建ParameterSample
对象时,c
需要是一个已经定义的Parameter
对象。
我将如何将其实现到我的ParameterSample
类中?
我试过的
我已经试着去做我自己的装潢师了,但是我不确定这是不是该走的路,因为我不完全明白我将如何使用这些。
我还考虑过在c
中添加一个@属性,它在每次调用Parameter
对象时都会创建一个新的Parameter
对象,但我觉得这样做并不合适,因为它可能会减慢代码的速度。
我还应该指出,上面的ParameterSample
类将在不同的类中继承,因此无论解决方案是什么,都应该能够在此设置中使用:
class Companion(ParameterSample)
def __init__(self, name):
self.name = name
super(Companion, self).__init__()
comp = Companion(name='Earth')
comp.set_parameter('a', value=1)
comp.set_parameter('b', value=3)
comp.c.value
>>> 4
发布于 2019-12-03 13:25:19
我无法让它在Python2中工作-- setattr
调用似乎从未将属性传播到子类(Companion
将没有c
属性)。
不过,我在Python 3方面更成功。由于您有两种参数类型(不同的和派生的),所以有两个类来实现行为是有意义的,而不是把它们都当作一个。
我添加了一个DerivedParameter
类,继承自接受dependents
参数(以及其父类‘args/kwargs)的Parameter
,但是重新定义value
和dist
以提供依赖行为:
class DerivedParameter(Parameter):
def __init__(self, name, dependents, **kwargs):
self._dependents = dependents
super().__init__(name, **kwargs)
@property
def value(self):
try:
return sum(x._value for x in self._dependents if x is not None)
except TypeError:
return None
@property
def dist(self):
try:
return sum(x._dist for x in self._dependents if x is not None)
except TypeError:
return None
然后,我调整了添加参数对象的方式:
class ParameterSample:
def __init__(self):
# Store as instance attributes to reference later
self.varied_params = ('a', 'b') # parameter names whose `value` attribute is varied
self.derived_params = ('c',) # parameter names whose `value` attribute is varied, but depends on `a.value` and `b.value`
# No more combined names
# create `Parameter` objects for each varied parameter name
for name in self.varied_params:
setattr(self, name, Parameter(name))
# Create `DerivedParameter` objects for each derived parameter
# Derived parameters depend on all `Parameter` objects. It wasn't
# clear if this was the desired behavior though.
params = [v for _, v in self.__dict__.items() if isinstance(v, Parameter)]
for name in self.derived_params:
setattr(self, name, DerivedParameter(name, params))
def set_parameter(self, name, **kwargs):
for key, val in kwargs.items():
if key == 'value':
key = '_'.join(['', key]) # add underscore to set `Parameter._value`
setattr(getattr(self, name), key, val) # basically does e.g. `self.a.value = 1`
这样,我就可以复制给定的示例所需的行为:
>>> comp = Companion(name='Earth')
>>> comp.set_parameter('a', value=1)
>>> comp.set_parameter('b', value=3)
>>> print(comp.c.value)
>>> print(comp.c.dist)
4
None
>>> comp.set_parameter('c', prior=(0,10))
>>> print(comp.c.prior)
(0, 10)
正如我在注释中指出的那样,上面的设计最终导致所有派生参数都使用所有不同的参数作为其依赖项--有效地使c
和潜在的d
完全相同。您应该能够很容易地通过一些参数/条件来修复这个问题。
总之,我不得不同意“错误-句法上的悔恨”。这是一种非常复杂的设计类的方法,最多也会使维护混乱。我强烈鼓励您重新考虑您的设计,并试图找到一种适应性强的通用解决方案,而不涉及像这样的属性的动态创建。
https://stackoverflow.com/questions/59164763
复制相似问题