我正在使用神经元模型。我正在设计的一个类是一个细胞类,它是神经元的拓扑描述(几个连接在一起的隔室)。它有许多参数,但它们都是相关的,例如:
轴突段数,顶端双分裂,体细胞长度,体细胞直径,顶端长度,分枝随机性,分枝长度等等...总共有大约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
答案 0 :(得分:62)
尝试这种方法:
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))
然后您可以像这样创建Neuron
:
n = Neuron(apical_bifibrications="special value")
答案 1 :(得分:16)
我认为这种方法没有任何问题 - 如果你需要15个参数来建模,你需要15个参数。如果没有合适的默认值,则在创建对象时必须传入所有15个参数。否则,您可以设置默认值,稍后通过设置器或直接更改它。
另一种方法是为某些常见类型的神经元创建子类(在您的示例中),并为某些值提供良好的默认值,或从其他参数中获取值。
或者您可以将神经元的某些部分封装在不同的类中,并将这些部分重用于您建模的实际神经元。即,你可以编写单独的类来建模突触,轴突,躯体等。
答案 2 :(得分:7)
您可以使用Python“dict”对象吗? http://docs.python.org/tutorial/datastructures.html#dictionaries
答案 3 :(得分:6)
有这么多参数表明这个课可能做了太多事情。
我建议你想把你的班级划分为几个班级,每个班级都需要你的一些参数。这样每个类都更简单,不会占用太多参数。
在不了解您的代码的情况下,我无法确切地说您应该如何拆分它。
答案 4 :(得分:5)
看起来你可以通过在LayerV构造函数之外构造诸如Axon
,Soma
和DendriticTree
之类的对象来减少参数的数量,并改为传递这些对象。
某些参数仅用于构建例如DendriticTree
,其他地方也使用其他地方,所以问题不是那么明确,但我肯定会尝试这种方法。
答案 5 :(得分:4)
您能提供一些您正在处理的示例代码吗?这将有助于了解您正在做什么,并尽快得到帮助。
如果只是你传递给类的参数使它变长,你就不必把它全部放在__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])
答案 6 :(得分:3)
在查看你的代码并意识到我不知道这些参数之间是如何相互关联的(因为我对神经科学的主题缺乏知识而烦恼)我会指出一本关于面向对象设计的非常好的书。 Steven F. Lott的面向对象设计中的建筑技巧是一本很好的读物,我认为它可以帮助你和其他任何人设计面向对象的程序。
它是根据知识共享许可证发布的,因此您可以免费使用,以下是PDF格式的链接http://homepage.mac.com/s_lott/books/oodesign/build-python/latex/BuildingSkillsinOODesign.pdf
我认为您的问题可以归结为课程的整体设计。有时,虽然很少,但是你需要大量的参数来初始化,而且这里的大多数响应都有详细的其他初始化方法,但是在很多情况下你可以把类分解成更容易处理和不那么繁琐的类
答案 7 :(得分:2)
这类似于迭代默认字典的其他解决方案,但它使用更紧凑的表示法:
class MyClass(object):
def __init__(self, **kwargs):
self.__dict__.update(dict(
arg1=123,
arg2=345,
arg3=678,
), **kwargs)
答案 8 :(得分:1)
您能提供更详细的用例吗?也许原型模式可行:
如果对象组中存在某些相似之处,原型模式可能有所帮助。 你是否有很多情况下,一群神经元就像另一群神经元在某种程度上不同? (即,而不是少数离散类, 你有大量的课程彼此略有不同。 )
Python是一种基于分类的语言,但正如您可以模拟基于类的语言一样 在基于原型的语言(如Javascript)中编程,您可以模拟 原型通过为您的类提供一个CLONE方法,创建一个新对象和 从父母那里填充其ivars。编写克隆方法以便关键字参数 传递给它覆盖“继承”参数,所以你可以用它来调用它 喜欢:
new_neuron = old_neuron.clone( branching_length=n1, branching_randomness=r2 )
答案 9 :(得分:1)
我从来没有处理过这种情况或这个话题。您的描述意味着,在您开发设计时,您可能会发现有许多其他类别会变得相关 - 隔离区是最明显的。如果它们本身就是作为类出现的,那么很可能你的一些参数成为这些附加类的参数。
答案 10 :(得分:0)
您可以为参数创建一个类。
相反,传递一堆参数,你传递一个类。
答案 11 :(得分:0)
我认为,在您的情况下,简单的解决方案是将高阶对象作为参数传递。
例如,在您的__init__
中,您有一个DendriticTree
,它使用来自主类LayerV
的几个参数:
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
)
您可以直接传递LayerV
对象(从而节省5个参数),而不是将这6个参数传递给DendriticTree
。
您可能希望在任何地方都可以访问此值,因此必须保存此DendriticTree
:
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()
通过这种方式,您可以将默认的DendriticTree
委派给专门用于解决此问题的类,而不是将此逻辑放在LayerV
的高阶类中。
最后,当您需要访问曾经传递给apical_bifibs
的{{1}}时,只需通过LayerV
对其进行访问即可。
通常,即使您要创建的类不是由几个类组成的清晰组合,您的目标仍然是找到一种逻辑方法来拆分参数。不仅使您的代码更整洁,而且主要是为了帮助人们了解这些参数的用途。在无法拆分它们的极端情况下,我认为拥有一个具有这么多参数的类是完全可以的。如果没有清晰的方法来拆分参数,那么最终可能会得到比15个参数列表更不清晰的结果。
如果您觉得创建一个将参数分组在一起的类是过大的选择,则可以简单地使用collections.namedtuple
,它可以将默认值设置为shown here。
答案 12 :(得分:0)
想重申一些人所说的话。如此多的参数没有错。特别是在科学计算/编程方面
例如,sklearn的KMeans++ clustering implementation具有11个可以初始化的参数。那样,有很多例子,没有错
答案 13 :(得分:0)
如果确保您需要这些参数,我会说没有错。如果你真的想让它更具可读性,我会推荐以下风格。
我不会说这是最佳实践或什么,它只是让其他人很容易地知道这个对象需要什么以及什么是选项。
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
我认为这种风格的好处是:
__init__
方法中所需的参数setter
不同,如果需要设置选项值,则不需要两行来构造对象。
缺点是,您在类中创建了比以前更多的方法。
示例:
la = LayerV("no_default", "necessary").b_else("sample_else")
毕竟,如果你有很多“necessary”和“no_default”参数,总想着是不是这个类(方法)做的事情太多了。
如果您的答案不是,请继续。