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

Python 参数过多的类:更好的设计策略?,python,oop,neuroscience,Python,Oop,Neuroscience,我正在研究神经元模型。我正在设计的一个类是cell类,它是神经元的拓扑描述(几个隔间连接在一起)。它有许多参数,但它们都是相关的,例如: 轴突节数、顶端双纤维、体长、体径、顶端长度、分枝随机性、分枝长度等。。。总共大约有15个参数 我可以将所有这些设置为一些默认值,但我的类有几行参数,看起来很疯狂。这种事情偶尔也会发生在其他人身上,是有更好的设计方法,还是我做的对 更新: 正如你们中的一些人所问,我已经附上了我的类代码,正如你们所看到的,这个类有大量的参数(>15),但它们都被使用,并且是定义单

我正在研究神经元模型。我正在设计的一个类是cell类,它是神经元的拓扑描述(几个隔间连接在一起)。它有许多参数,但它们都是相关的,例如:

轴突节数、顶端双纤维、体长、体径、顶端长度、分枝随机性、分枝长度等。。。总共大约有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


可以为参数创建一个类


不是传递一组参数,而是传递一个类。

您可以使用Python的“dict”对象吗?

更新:这种方法可能适合您的具体情况,但肯定有其缺点,请参阅

尝试以下方法:

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))
然后,您可以创建一个
神经元
,如下所示:

n = Neuron(apical_bifibrications="special value")

我要说的是,这种方法没有错——如果你需要15个参数来建模,你需要15个参数。如果没有合适的默认值,则在创建对象时必须传入所有15个参数。否则,您可以只设置默认值,然后通过setter或直接更改它

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


或者,您可以将神经元的部分封装在单独的类中,并将这些部分重新用于您建模的实际神经元。也就是说,您可以编写单独的类来对突触、轴突、躯体等进行建模。

您能提供一些您正在研究的示例代码吗?这将有助于了解你正在做什么,并尽快得到帮助

如果只是传递给类的参数使其变长,则不必将其全部放在
\uuuuu init\uuuu
中。您可以在创建类后设置参数,也可以将一个充满参数的字典/类作为参数传递

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])

你能给出一个更详细的用例吗?也许原型模式会起作用:

如果对象组中存在一些相似之处,那么原型模式可能会有所帮助。 在很多情况下,一个神经元群体和另一个神经元群体一样,只是在某些方面有所不同吗?(即,不是有少量离散类, 您有大量的类,这些类彼此略有不同。)

Python是一种基于类的语言,但正如您可以模拟基于类的语言一样 使用基于原型的语言(如Javascript)编程,您可以模拟 通过给类一个克隆方法来创建原型,该方法创建一个新对象并 从父级填充其IVAR。编写clone方法,使关键字参数 传递给它的参数将覆盖“继承的”参数,因此您可以使用某些内容调用它 比如:


有这么多参数表明类可能做了太多的事情

我建议您将类划分为几个类,每个类都接受一些参数。这样,每个类都更简单,不需要太多参数


如果不了解更多关于代码的信息,我就不能确切地说应该如何分割代码。

我从来没有处理过这种情况或这个主题。您的描述对我来说意味着,在开发设计时,您可能会发现,有许多其他类将变得相关-隔间是最明显的。如果这些确实以类的形式出现,那么您的一些参数很可能会成为这些附加类的参数。

在查看您的代码并意识到我不知道这些参数中的任何一个是如何相互关联的(完全是因为我缺乏神经科学方面的知识)我想给大家介绍一本关于面向对象设计的好书。Steven F.Lott的《面向对象设计中的构建技巧》是一本优秀的读物,我认为这本书对你和其他任何人设计面向对象的程序都有帮助

它是根据知识共享许可证发布的,因此您可以免费使用,这里有一个PDF格式的链接


我认为你的问题可以归结为你的课程的总体设计。有时,虽然很少,但您需要大量的参数来初始化,这里的大多数响应都有详细的其他初始化方式,但在很多情况下,您可以将类分解为更易于处理且不太麻烦的类。

这与通过默认字典进行迭代的其他解决方案类似,但它使用了更紧凑的表示法:

class MyClass(object):

    def __init__(self, **kwargs):
        self.__dict__.update(dict(
            arg1=123,
            arg2=345,
            arg3=678,
        ), **kwargs)

看起来您可以通过在LayerV构造函数之外构造对象(如
Axon
Soma
DendriticTree
)并传递这些对象来减少参数的数量


一些参数仅用于构造,例如树状体,其他参数也用于其他地方,因此问题不是很明确,但我肯定会尝试这种方法。

在我看来,在您的情况下,简单的解决方法是将高阶对象作为参数传递

例如,在
\uuuu init\uuuu
中有一个
树状体视图class MyClass(object):

    def __init__(self, **kwargs):
        self.__dict__.update(dict(
            arg1=123,
            arg2=345,
            arg3=678,
        ), **kwargs)
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
)
class LayerV(__Cell):
    def __init__(self, main_apical_dendrite, ...):
        self.main_apical_dendrite = main_apical_dendrite        
class LayerV(__Cell):
    def __init__(self, main_apical_dendrite=None, ...):
        self.main_apical_dendrite = main_apical_dendrite or DendriticTree()
class LayerV(__Cell):
    # author: {name, url} who made this info
    def __init__(self, no_default_params, some_necessary_params):
        self.necessary_param = some_necessary_params
        self.no_default_param = no_default_params
        self.something_else = "default"
        self.some_option = "default"
    
    def b_option(self, value):
        self.some_option = value
        return self

    def b_else(self, value):
        self.something_else = value
        return self