매개 변수가 너무 많은 클래스 : 더 나은 설계 전략?


78

저는 뉴런 모델로 작업하고 있습니다. 제가 디자인하고있는 클래스 중 하나는 뉴런의 토폴로지 설명 인 셀 클래스입니다 (여러 구획이 서로 연결되어 있음). 매개 변수가 많지만 모두 관련이 있습니다. 예를 들면 다음과 같습니다.

축삭 분절의 수, 정점 bifibrications, 체세포 길이, 체체 직경, 정점 길이, 분기 임의성, 분기 길이 등 ... 총 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

1
@Chris Walton : 우리가 그것을 찬성하고 댓글을 달 수 있도록 답변을 게시 해주세요.
S.Lott

4
당신의 코드를 살펴보면 ... 내가 바꿀 게 너무 많지는 않습니다. 매개 변수를 별도의 클래스 또는 사전에 넣을 수 있으며, 이는 또한 기본값 세트 제공을 단순화합니다. 또는 뉴런이 생성되는 함수 / 방법에서 축삭, 소마 및 주 수상 돌기를 생성 한 다음 매개 변수 대신 객체를 전달할 수 있습니다. 하지만 수업은 이대로 괜찮다고 생각합니다. 문제가 생기면 여기에서 다시 방문하겠습니다.
onitake

onitake 조언에 감사드립니다!
Mike Vella

답변:


72

업데이트 : 이 접근 방식은 특정 경우에 적합 할 수 있지만 확실히 단점 이 있습니다. kwargs는 반 패턴입니까?

이 접근 방식을 시도하십시오.

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

4
for루프가 두 라인으로 대체 될 수있다 self.__dict__.update(prop_defaults); self.__dict__.update(kwargs). 또는 if문을 setattr(self, prop, kwargs.get(prop, default)).
Sven Marnach 2011

1
@Sven Marnach : 귀하의 첫 번째 제안은 동등하지 않습니다 kwargs. 비 속성이 포함 된 경우를 고려하십시오 . 그래도 두 번째 접근 방식을 좋아합니다!
blubb 2011

@blubb 사용자가 쓸모없는 / 인식 할 수없는 매개 변수로 인스턴스화하면 어떻게됩니까?
slaw

8
그러나 OP의 원래 구식 구현에 어떤 문제가 있습니까? 이 답변의 접근 방식보다 더 읽기 쉽습니다. @onitake의 답변을
RayLuo

3
이것은 끔찍한 해결책입니다. 이는 귀하의 코드를 사용하는 누군가가 실제 소스 코드로 가서 무엇이 필요하거나 필요하지 않은지 결정해야 함을 의미합니다. 이것은 끔찍한 파이썬 안티 패턴입니다. 클래스 init 메소드를 여러 줄로 나누고 그렇게 유지하십시오. 훨씬 더 사용자 친화적입니다.
Jdban101

19

이 접근 방식에는 아무런 문제가 없다고 말하고 싶습니다. 무언가를 모델링하기 위해 15 개의 매개 변수가 필요하다면 15 개의 매개 변수가 필요합니다. 적절한 기본값이 없으면 개체를 만들 때 15 개의 매개 변수를 모두 전달해야합니다. 그렇지 않으면 기본값을 설정하고 나중에 setter를 통해 또는 직접 변경할 수 있습니다.

또 다른 접근 방식은 특정 일반적인 종류의 뉴런 (예 :)에 대한 하위 클래스를 만들고 특정 값에 대해 좋은 기본값을 제공하거나 다른 매개 변수에서 값을 파생하는 것입니다.

또는 뉴런의 일부를 별도의 클래스로 캡슐화하고 이러한 부분을 모델링하는 실제 뉴런에 재사용 할 수 있습니다. 즉, 시냅스, 축삭, 소마 등을 모델링하기위한 별도의 클래스를 작성할 수 있습니다.


7

Python "dict"객체를 사용할 수 있습니까? http://docs.python.org/tutorial/datastructures.html#dictionaries


기본적으로 dict를 매개 변수 블록으로 사용하십시오. 이는 거의 동일한 매개 변수를 사용하여 일련의 객체를 생성해야하는 코드를 단순화합니다.
Mike DeSimone

사전은 IDE 코드 완성을 쉽게 만들지 못합니다. 따라서 'abb'를 의미 할 때 'aba'라는 이름으로 속성을 설정할 수 있습니다. 저는 개인적으로 collections.namedtuple을 대신 선호합니다. docs.python.org/3/library/…
JGFMK

6

매개 변수가 너무 많으면 클래스가 너무 많은 일을하고 있음을 나타냅니다.

클래스를 여러 클래스로 나누고 각 클래스는 몇 가지 매개 변수를 사용하는 것이 좋습니다. 이렇게하면 각 클래스가 더 간단하고 많은 매개 변수를 사용하지 않습니다.

코드에 대해 더 많이 알지 못하면 코드를 어떻게 분할해야하는지 정확히 말할 수 없습니다.


22
동의하지 않습니다. 일반적으로 많은 매개 변수가 문제 분해 불량에 대한 표시이지만 과학적 영역에서는 이것이 내 경험에 적용되지 않습니다. 자유도가 10 인 함수를 모델링해야하는 경우 10 개의 매개 변수가 있어야합니다. 그게 전부입니다.
blubb

8
@Simon Stelling :하지만. 어떤 경우에는 실제로 15 자유도가 아니라 각각 10 자유도가있는 두 개의 겹치는 모델이 있습니다. "분해 고려"는 "맹목적으로 분해"를 의미하지 않습니다. 15 개의 속성에 대한 표면적 설명은 애플리케이션에 따라 분해 될 수 있음을 의미합니다. 복잡성을 없앨 방법은 없습니다. 그러나 구획화 될 수 있습니다.
S.Lott

1
@Simon, 그럴 수도 있습니다. 그러나 적어도 그것이 분해 될 수 있는지 고려할 가치가 있습니다.
Winston Ewert

6

Axon, Soma및 같은 객체를 구성하여 인수 수를 줄일 수있는 것 같습니다.DendriticTree LayerV 생성자의 외부, 대신 그 객체를 전달합니다.

매개 변수 중 일부는 예를 들어 구성에만 사용되며 DendriticTree다른 매개 변수는 다른 장소에서도 사용되므로 문제는 명확하지 않지만 확실히 시도해 볼 것입니다.


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

self.__dict__ = kwargs언급했듯이 나쁜 생각입니다. 내 대답에는이 문제에 대한 간단한 해결책이 있습니다.
blubb 2011

그래, 우리 둘 다 동시에 대답했고 나는 서두르고 있었다. 나는 그것을 고쳤다.
Nate

그것은 세부 사항이지만 당신은 대신 사용 if hasattr(self, key):합니다self.__dict__.keys()
blubb

아, 명령이 있다는 걸 알고 있었어요. 머리 위에서 기억이 안 나네요. 감사.
Nate

3

여러분의 코드를 살펴보고 그 매개 변수가 서로 어떤 관련이 있는지 전혀 모르겠다는 것을 깨달은 후에 (신경 과학 주제에 대한 지식이 부족하기 때문에 혼자서) 객체 지향 설계에 대한 아주 좋은 책을 알려 드리겠습니다. Steven F. Lott의 Object Oriented Design의 Building Skills in Object Oriented Design은 훌륭한 읽기이며 저는 여러분과 다른 사람이 객체 지향 프로그램을 배치하는 데 도움이 될 것이라고 생각합니다.

크리에이티브 커먼즈 라이선스에 따라 출시되었으므로 무료로 사용할 수 있습니다. 여기에 PDF 형식의 링크가 있습니다 . http://homepage.mac.com/s_lott/books/oodesign/build-python/latex/BuildingSkillsinOODesign. pdf

나는 당신의 문제가 수업의 전반적인 디자인으로 귀결된다고 생각합니다. 매우 드물지만 초기화를 위해 많은 인수가 필요하며 여기에있는 대부분의 응답에는 다른 초기화 방법이 자세히 설명되어 있지만 많은 경우 클래스를 처리하기 더 쉽고 덜 성가신 클래스로 나눌 수 있습니다. .


1
이 책은 이제 여기에서 찾을 수 있습니다. itmaybeahack.com/homepage/books/oodesign.html#book-oodesign
Mausy5043

3

이것은 기본 사전을 반복하는 다른 솔루션과 유사하지만 더 간결한 표기법을 사용합니다.

class MyClass(object):

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

1

더 자세한 사용 사례를 제공 할 수 있습니까? 프로토 타입 패턴이 작동 할 수 있습니다.

개체 그룹에 유사점이 있다면 프로토 타입 패턴이 도움이 될 수 있습니다. 한 뉴런 집단이 어떤면에서 다른 점을 제외하고는 다른 집단과 똑같은 경우가 많이 있습니까? (즉, 적은 수의 개별 클래스를 갖는 대신 서로 약간 다른 많은 수의 클래스가 있습니다.)

Python은 클래스 기반 언어이지만 Javascript와 같은 프로토 타입 기반 언어로 클래스 기반 프로그래밍을 시뮬레이션 할 수있는 것처럼 클래스에 CLONE 메서드를 제공하여 프로토 타입을 시뮬레이션 할 수 있습니다.이 메서드는 새 객체를 만들고 부모로부터 ivar를 채 웁니다. 전달 된 키워드 매개 변수가 "상속 된"매개 변수를 재정의하도록 clone 메소드를 작성하여 다음과 같이 호출 할 수 있습니다.

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

1

나는이 상황이나이 주제를 다룰 필요가 없었습니다. 귀하의 설명은 설계를 개발할 때 관련성이있는 추가 클래스가 많이 있음을 알 수 있음을 암시합니다. 구획이 가장 분명합니다. 이것들이 그 자체로 클래스로 등장한다면, 일부 매개 변수가 이러한 추가 클래스의 매개 변수가 될 가능성이 있습니다.


0

매개 변수에 대한 클래스를 만들 수 있습니다.

여러 매개 변수를 전달하는 대신 하나의 클래스를 전달합니다.


5
"매개 변수 클래스"를 하나만 생성하면 문제가 새 클래스에 위임되었으므로 도움이되지 않습니다.
blubb 2011

1
Simon이 지적했듯이 이것은 실제로 복잡성을 줄이지 않고 복잡한 계층을 추가하는 것처럼 보입니다.
Mike Vella

0

제 생각에, 귀하의 경우 쉬운 해결책은 고차 객체를 매개 변수로 전달하는 것입니다.

예를 들어, 메인 클래스의 여러 인수를 사용 __init__하는가 있습니다 .DendriticTreeLayerV

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
)

이 6 개의 인수를 전달하는 대신 객체를 직접 LayerV전달합니다 DendriticTree(따라서 5 개의 인수를 저장함).

이 값을 어디서나 액세스 할 수 있기를 원할 것이므로 다음을 저장해야합니다 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전달하는 데 사용했던에 LayerV액세스해야 할 때를 통해 액세스하기 만하면됩니다 self.main_apical_dendrite.bifibs.

일반적으로 만들고있는 클래스가 여러 클래스의 명확한 구성이 아니더라도 매개 변수를 분할하는 논리적 방법을 찾는 것이 목표입니다. 코드를 더 깔끔하게 만드는 것뿐만 아니라 사람들이이 매개 변수가 무엇에 사용되는지 이해하는 데 도움이됩니다. 분할 할 수없는 극단적 인 경우에는 그렇게 많은 매개 변수를 가진 클래스가 있어도 무방하다고 생각합니다. 인수를 분할하는 명확한 방법이 없다면 아마도 15 개의 인수 목록보다 더 명확하지 않은 것으로 끝날 것입니다.

매개 변수를 그룹화하는 클래스를 만드는 것이 과도하다고 생각되면 여기에 표시된collections.namedtuple 대로 기본값을 가질 수있는 것을 간단히 사용할 수 있습니다 .


0

많은 사람들이 말한 것을 반복하고 싶습니다. 그 정도의 매개 변수에는 문제가 없습니다. 특히 과학 컴퓨팅 / 프로그래밍과 관련하여

예를 들어, 초기화 할 수있는 11 개의 매개 변수가있는 sklearn의 KMeans ++ 클러스터링 구현 을 살펴보십시오 . 그렇게 많은 예가 있고 그들에게 잘못된 것은 없습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.