Подтвердить что ты не робот

Класс со слишком большим количеством параметров: лучшая стратегия дизайна?

Я работаю с моделями нейронов. Один класс, который я проектирую, - это класс клеток, который является топологическим описанием нейрона (несколько отсеков соединены вместе). Он имеет множество параметров, но все они имеют значение, например:

количество сегментов аксона, апикальные бифибры, соматическая длина, соматический диаметр, апикальная длина, ветвящаяся случайность, длина ветвления и т.д. и т.д.... всего около 15 параметров!

Я могу установить все эти значения по умолчанию, но мой класс выглядит сумасшедшим с несколькими строками для параметров. Такие вещи иногда должны происходить и с другими людьми, есть ли некоторые очевидные пути для разработки этого или я делаю правильные вещи?

UPDATE: Поскольку некоторые из вас спросили, что я прикрепил свой код к классу, так как вы можете видеть, что этот класс имеет огромное количество параметров ( > 15), но все они используются и необходимы для определения топологии ячейки. Проблема состоит в том, что создаваемый ими физический объект очень сложный. Я добавил изображение объектов, созданных этим классом. Как опытные программисты могли бы сделать это по-другому, чтобы избежать так много параметров в определении?

enter image description here

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
4b9b3361

Ответ 1

Попробуйте этот подход:

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

Ответ 2

Я бы сказал, что с этим подходом нет ничего плохого - если вам нужно 15 параметров для моделирования чего-то, вам нужно 15 параметров. И если нет подходящего значения по умолчанию, вы должны передать все 15 параметров при создании объекта. В противном случае вы можете просто установить значение по умолчанию и изменить его позже через сеттер или напрямую.

Другой подход заключается в создании подклассов для некоторых распространенных типов нейронов (в вашем примере) и обеспечения хороших значений по умолчанию для определенных значений или получения значений из других параметров.

Или вы можете инкапсулировать части нейрона в отдельные классы и повторно использовать эти части для реальных моделей нейронов. I.e., вы можете написать отдельные классы для моделирования синапса, аксона, сомы и т.д.

Ответ 4

Имея так много параметров, предположим, что класс, вероятно, делает слишком много вещей.

Я предлагаю вам разделить класс на несколько классов, каждый из которых принимает некоторые из ваших параметров. Таким образом, каждый класс проще и не будет принимать столько параметров.

Не зная больше о вашем коде, я не могу точно сказать, как вы должны разделить его.

Ответ 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])

Ответ 6

Вы можете создать класс для своих параметров.

Вместо передачи нескольких параметров вы передаете один класс.

Ответ 7

Можете ли вы дать более подробный пример использования? Возможно, образец прототипа будет работать:

Если есть несколько сходств в группах объектов, может помочь образец прототипа. У вас есть много случаев, когда одно население нейронов подобно другому, кроме как-то иначе? (то есть вместо того, чтобы иметь небольшое число дискретных классов, у вас есть большое количество классов, которые немного отличаются друг от друга. )

Python - это классифицированный язык, но так же, как вы можете имитировать класс программирование на языке, основанном на прототипе, таком как Javascript, вы можете имитировать прототипы, предоставив вашему классу метод CLONE, который создает новый объект и заполняет свои ивары от родителя. Напишите метод clone, чтобы параметры ключевых слов переданные ему, переопределяют "унаследованные" параметры, поэтому вы можете называть это чем-то например:

new_neuron = old_neuron.clone( branching_length=n1, branching_randomness=r2 )

Ответ 8

Это похоже на другие решения, которые повторяют словарь по умолчанию, но использует более компактную нотацию:

class MyClass(object):

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

Ответ 9

Похоже, вы можете сократить количество аргументов, построив объекты, такие как Axon, Soma и DendriticTree вне конструктора LayerV, и вместо этого передайте эти объекты.

Некоторые из параметров используются только при конструировании, например. DendriticTree, другие также используются в других местах, поэтому проблема не столь ясна, но я определенно попытаюсь использовать этот подход.

Ответ 10

Мне никогда не приходилось иметь дело с этой ситуацией или этой темой. Ваше описание подразумевает, что вы, как вы разрабатываете дизайн, можете найти, что есть ряд дополнительных классов, которые станут релевантными - наиболее очевидным является отсек. Если они появляются как классы сами по себе, вероятно, некоторые из ваших параметров станут параметрами этих дополнительных классов.

Ответ 11

Просматривая свой код и понимая, я понятия не имею, как какой-либо из этих параметров относится друг к другу (из-за моего отсутствия знаний о неврологии) я бы указал на очень хорошую книгу по объектно-ориентированному дизайну, Навыки построения в объектно-ориентированном дизайне Стивена Ф. Лотта - отличное чтение, и я думаю, это поможет вам и всем остальным в разработке объектно-ориентированных программ.

Он выпущен под лицензией Creative Commons, поэтому вы можете использовать его здесь, вот его ссылка в формате PDF http://homepage.mac.com/s_lott/books/oodesign/build-python/latex/BuildingSkillsinOODesign.pdf

Я думаю, что ваша проблема сводится к общему дизайну ваших классов. Иногда, хотя и очень редко, для инициализации требуется множество аргументов, и в большинстве ответов здесь подробно описаны другие способы инициализации, но во многих случаях вы можете разбить класс на более легкие для обработки и менее громоздкие классы.