参数太多的类:更好的设计策略?

Mik*_*lla 69 python oop neuroscience

我正在使用神经元模型.我正在设计的一个类是一个细胞类,它是神经元的拓扑描述(几个连接在一起的隔室).它有许多参数,但它们都是相关的,例如:

轴突节段数,顶端双分裂,体长,体细胞直径,顶端长度,分枝随机性,分枝长度等等...总共约有15个参数!

我可以将所有这些设置为某个默认值,但我的课看起来很疯狂,有几行参数.这种事情也偶尔会发生在其他人身上,是否有一些明显更好的方法来设计这个或者我做对了吗?

更新: 正如你们中的一些人已经要求我已经为类添加了我的代码,因为你可以看到这个类有大量的参数(> 15)但它们都被使用并且是定义单元格拓扑所必需的.问题主要在于他们创建的物理对象非常复杂.我附上了这个类产生的对象的图像表示.有经验的程序员如何以不同的方式做到这一点以避免定义中的这么多参数?

在此输入图像描述

class LayerV(__Cell):

    def __init__(self,somatic_dendrites=10,oblique_dendrites=10,
                somatic_bifibs=3,apical_bifibs=10,oblique_bifibs=3,
                L_sigma=0.0,apical_branch_prob=1.0,
                somatic_branch_prob=1.0,oblique_branch_prob=1.0,
                soma_L=30,soma_d=25,axon_segs=5,myelin_L=100,
                apical_sec1_L=200,oblique_sec1_L=40,somadend_sec1_L=60,
                ldecf=0.98):

        import random
        import math

        #make main the regions:
        axon=Axon(n_axon_seg=axon_segs)

        soma=Soma(diam=soma_d,length=soma_L)

        main_apical_dendrite=DendriticTree(bifibs=
                apical_bifibs,first_sec_L=apical_sec1_L,
                L_sigma=L_sigma,L_decrease_factor=ldecf,
                first_sec_d=9,branch_prob=apical_branch_prob)

        #make the somatic denrites

        somatic_dends=self.dendrite_list(num_dends=somatic_dendrites,
                       bifibs=somatic_bifibs,first_sec_L=somadend_sec1_L,
                       first_sec_d=1.5,L_sigma=L_sigma,
                       branch_prob=somatic_branch_prob,L_decrease_factor=ldecf)

        #make oblique dendrites:

        oblique_dends=self.dendrite_list(num_dends=oblique_dendrites,
                       bifibs=oblique_bifibs,first_sec_L=oblique_sec1_L,
                       first_sec_d=1.5,L_sigma=L_sigma,
                       branch_prob=oblique_branch_prob,L_decrease_factor=ldecf)

        #connect axon to soma:
        axon_section=axon.get_connecting_section()
        self.soma_body=soma.body
        soma.connect(axon_section,region_end=1)

        #connect apical dendrite to soma:
        apical_dendrite_firstsec=main_apical_dendrite.get_connecting_section()
        soma.connect(apical_dendrite_firstsec,region_end=0)

        #connect oblique dendrites to apical first section:
        for dendrite in oblique_dends:
            apical_location=math.exp(-5*random.random()) #for now connecting randomly but need to do this on some linspace
            apsec=dendrite.get_connecting_section()
            apsec.connect(apical_dendrite_firstsec,apical_location,0)

        #connect dendrites to soma:
        for dend in somatic_dends:
            dendsec=dend.get_connecting_section()
            soma.connect(dendsec,region_end=random.random()) #for now connecting randomly but need to do this on some linspace

        #assign public sections
        self.axon_iseg=axon.iseg
        self.axon_hill=axon.hill
        self.axon_nodes=axon.nodes
        self.axon_myelin=axon.myelin
        self.axon_sections=[axon.hill]+[axon.iseg]+axon.nodes+axon.myelin
        self.soma_sections=[soma.body]
        self.apical_dendrites=main_apical_dendrite.all_sections+self.seclist(oblique_dends)
        self.somatic_dendrites=self.seclist(somatic_dends)
        self.dendrites=self.apical_dendrites+self.somatic_dendrites
        self.all_sections=self.axon_sections+[self.soma_sections]+self.dendrites
Run Code Online (Sandbox Code Playgroud)

blu*_*ubb 66

试试这种方法:

class Neuron(object):

    def __init__(self, **kwargs):
        prop_defaults = {
            "num_axon_segments": 0, 
            "apical_bifibrications": "fancy default",
            ...
        }

        for (prop, default) in prop_defaults.iteritems():
            setattr(self, prop, kwargs.get(prop, default))
Run Code Online (Sandbox Code Playgroud)

然后你可以创建一个Neuron这样的:

n = Neuron(apical_bifibrications="special value")
Run Code Online (Sandbox Code Playgroud)

  • 但是,OP的原始旧学校实施有什么问题?它比这个答案的方法更具可读性.我建议关注@onitake的[回答](http://stackoverflow.com/a/5899352/728675) (5认同)
  • 这是一个可怕的解决方案。这意味着使用您的代码的人必须进入实际的源代码以确定需要/不需要什么。这是一个可怕的 python 反模式只需将类 __init__ 方法拆分为多行并以这种方式维护它。更加用户友好。 (4认同)
  • `for`循环可以用两行替换`self .__ dict __.update(prop_defaults); 自.__字典__.更新(kwargs)`.或者,您可以用`setattr(self,prop,kwargs.get(prop,default))`替换`if`语句. (3认同)

oni*_*ake 16

我认为这种方法没有任何问题 - 如果你需要15个参数来建模,你需要15个参数.如果没有合适的默认值,则在创建对象时必须传入所有15个参数.否则,您可以设置默认值,稍后通过设置器或直接更改它.

另一种方法是为某些常见类型的神经元(在您的示例中)创建子类,并为某些值提供良好的默认值,或从其他参数派生值.

或者您可以将神经元的一部分封装在不同的类中,并将这些部分重用于您建模的实际神经元.也就是说,你可以编写单独的类来建模突触,轴突,躯体等.


Win*_*ert 6

有这么多参数表明该课程可能做了太多事情.

我建议你要把你的班级划分为几个班级,每个班级都要考虑你的一些参数.这样每个类都更简单,不会占用太多参数.

在不了解您的代码的情况下,我无法确切地说您应该如何拆分它.

  • 我不同意.通常许多参数都表明问题分解不佳,但在科学领域,这并不符合我的经验.如果你需要建立一个具有10个自由度的函数,你必须有10个参数,就是这样...... (19认同)
  • @Simon Stelling:但是.在某些情况下,确实没有15个自由度,而是两个重叠模型,每个模型具有10个自由度."考虑分解"并不意味着"盲目分解".这意味着可以基于应用程序对15个属性的表面描述进行分解.没有办法让复杂性消失.但是,它可能是分隔的. (8认同)

Nat*_*ate 5

您能否提供一些您正在处理的示例代码?这将有助于您了解自己在做什么,并尽快获得帮助。

如果只是传递给类的参数使它变长,则不必全部放入__init__。您可以在创建类后设置参数,或将充满参数的字典/类作为参数传递。

class MyClass(object):

    def __init__(self, **kwargs):
        arg1 = None
        arg2 = None
        arg3 = None

        for (key, value) in kwargs.iteritems():
            if hasattr(self, key):
                setattr(self, key, value)

if __name__ == "__main__":

    a_class = MyClass()
    a_class.arg1 = "A string"
    a_class.arg2 = 105
    a_class.arg3 = ["List", 100, 50.4]

    b_class = MyClass(arg1 = "Astring", arg2 = 105, arg3 = ["List", 100, 50.4])
Run Code Online (Sandbox Code Playgroud)


Mar*_*ani 5

看起来你可以通过构建物体,如减少参数的个数Axon,SomaDendriticTree外面的LayerV构造,并通过这些对象来代替.

一些参数仅用于构建,例如DendriticTree,其他参数也用于其他地方,所以问题不是那么明确,但我肯定会尝试这种方法.