从其他实例属性派生的类实例属性

我很抱歉,标题不太清楚,我想不出一种用句子来描述我的问题的方法。我正在python2.7中构建一些代码,如下所述。

最小的工作示例

我的代码有一个Parameter类,该类实现了namevalue之类的属性,

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对象中的某些对象具有使用value函数设置的属性(例如errorParameter.set_parameter()),而其他Parameter对象未明确设置,但是它们的valuedist属性依赖取决于其他设置的其他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,Parameter(name))

    def set_parameter(self,**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对象,该对象具有与ab相同的属性,但是其value和{{1} }根据dista的当前属性进行更新。

但是,我还应该提到我希望能够为参数b设置允许的优先范围,例如c对其值进行任何调用之前,因此parobj.set_parameter('c',prior=(0,10))必须是创建c对象时已经定义的Parameter对象。

如何将其实现到我的ParameterSample类中?

我尝试过的

我试图研究制作自己的装饰器,但是我不确定这是否可行,因为我不完全了解如何使用它们。

我还考虑过在ParameterSample上添加@property来在每次调用时创建一个新的c对象,但是我觉得这不是可行的方法,因为它可能会减慢速度代码。

我还应该注意,上面的Parameter类将在另一个类中继承,因此无论采用什么解决方案,都应能够在此设置中使用它:

ParameterSample
sytsoft 回答:从其他实例属性派生的类实例属性

我无法在Python 2中使用此功能-setattr调用似乎从未将属性传播到子类(Companion没有c属性)。

尽管我在使用Python 3方面更加成功。由于您有两种参数类型(变量与派生),因此IMO有两个类可以实现行为,而不是将它们全部视为一个。

我添加了一个DerivedParameter类,该类从Parameter继承,该类接受一个dependents参数(及其父类的args / kwargs),但是重新定义了value和{ {1}}给予依赖行为:

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,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,DerivedParameter(name,params))

    def set_parameter(self,**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) 和潜在的{{1} }相同。您应该能够使用某些参数/条件很容易地解决此问题。

总的来说,我必须同意@Error-句法Re悔。这是设计类的一种相当复杂的方法,会使维护充其量是令人困惑的。我强烈建议您重新考虑设计,并尝试找到一种不涉及动态创建此类属性的适应性通用解决方案。

本文链接:https://www.f2er.com/2982643.html

大家都在问