2011-07-27 7 views
5

중 하나의 방법을 사용하여 I가 다음 코드를 왜 여전히 A.PickTargetuseA=FalseC.Update를 호출을다중 상속 및 기본 클래스

class A(object): 
    def __init__(self): 
     self.name = "A" 
     super(A, self).__init__() 

    def Update(self): 
     print "Update A" 
     self.PickTarget() 

    def PickTarget(self): 
     print "PickTarget A" 

class B(object): 
    def __init__(self): 
     self.name = "B" 
     super(B, self).__init__() 

    def Update(self): 
     print "Update B" 
     self.PickTarget() 

    def PickTarget(self): 
     print "PickTarget B" 

class C(A, B): 
    def __init__(self): 
     super(C, self).__init__() 

    def Update(self, useA): 
     if useA: 
      A.Update(self) 
     else: 
      B.Update(self) 

c = C() 

c.Update(useA = True) 
# prints: 
# Update A 
# PickTarget A 

c.Update(useA = False) 
# prints: 
# Update B 
# PickTarget A 

를 호출합니까? 내가 어떻게 작동하게 할 수 있습니까? (예 : B.Update는 항상 B.PickTarget을 호출합니다) 나는 이것이 전에 질문 받았다고 확신하지만 아무것도 찾지 못했다. 아마도 내가 무엇을 검색해야할지 모르겠다.

+0

여전히 재 설계가 필요하다고 생각되지만,이 글에서는 이름 맹 글링이이 문제에 대해 상당히 유연한 해결책을 제공한다는 점을 지적 할 것입니다. [내 편집] (http://stackoverflow.com/questions/6849519/multiple-inheritance-and-using-a-method-of-one-of-the-base-classes/6849613#6849613)을 참조하십시오. – senderle

답변

7

AB보다 앞에 있고 C의 기본 클래스가 있기 때문입니다.

self.PickTarget()B.Update(self)이 아닌 B.PickTarget(self)이 사용해야합니다. 그렇지 않은 경우 ABC의 정의로 바꾸십시오.

편집 : B 항상 B에서 메소드를 호출과 A 항상 A에서 메소드를 호출하는 의도 된 행동 인 경우 두 번째 양식은 '아무튼으로

, 그것은 self.method() 대신 A.method(self)를 사용하는 올바른입니다 methodA에 있음을 의미합니다.

수업을 다시 디자인해야합니다. A에는 로봇을 무작위로 움직이고 다른 기본 동작을 정의하는 이동 방법이 있어야합니다. BA의 하위 클래스 여야하며 경로가 없으면 super(B, self).move()을 호출하고 그렇지 않은 경우 경로를 이동하는 move 메서드가 있어야합니다. 이것은 조건에 대한 메소드를 재정의하는 적절한 방법입니다.

+0

A 및 B를 수정하지 않고도이 작업을 수행 할 수있는 방법이 없습니까? 이것은 단지 예제 코드 일뿐입니다. 실제 코드에서는 A와 B도 독립 실행 형으로 사용됩니다. (실제 코드는 로봇을위한 간단한 인공 지능입니다 - A는 로봇을 무작위로 움직이고, B는 로봇이 경로를 따라 가게하고, C는 로봇이 경로를 가지고 있는지 확인합니다. 그렇지 않으면 A.Update를 호출합니다. 그렇다면 B를 호출합니다. .Update.이 문제를 해결할 더 좋은 방법이 있다면, 나는 모두 귀야.) – combatdave

+0

내가 말했듯이, 당신은 단지'C'의 정의에서'A'와'B'를 바꿀 수 있습니다. 또한 의도 한 행동은'B'가 항상'B'의 메소드를 호출하고'A'는 항상'A'의 메소드를 호출하는 것이고, 어떤 경우에는'A.method (self)'를 사용하는 것이 __correct__ 인 것 같습니다 두 번째 형태가'method'가'A'에 있음을 의미하지는 않기 때문에'self.method()'를 사용하십시오. – agf

+0

나는 본다! 이제 이것이 의미가 있기 시작합니다. 클래스의 속성에 액세스하려면 어떻게해야합니까? 예를 들어, A.Update에서 self.name에 액세스 - self.name 또는 A.name이어야합니까? – combatdave

2

개체의 메서드가 호출 될 때마다 python은 "메서드 확인 순서"(MRO)를 사용하여 호출 할 메서드의 버전을 결정합니다. 이 경우 명시 적으로 A.Update()을 호출했지만 A.Update()은 명시 적으로 A.PickTarget을 호출하지 않습니다. 방금 self.PickTarget()이 호출됩니다. 이 객체는 C이므로 C.PickTarget(self)과 같습니다. C.PickTarget()이 상속되며 C MRO는이 경우 A.PickTargetPickTarget의 버전임을 나타냅니다.

당신은이 같은 C의 MRO 볼 수 있습니다 :

MRO here에 슈퍼 유익한 기사가있다
>>> C.__mro__ 
(<class '__main__.C'>, <class 'foo.A'>, <class 'foo.B'>, <type 'object'>) 

. 당신이 원하는 행동을 얻는 방법에 대해서는


- 음, 꽤 분명 많은 방법이 있고, 같은 시간에, 더 좋은 것들 (내가 생각할 수 없음). 나는 이것이 정말로 좋은 디자인이라고 생각하지 않는다. 다중 상속의 주요 포인트는 C의 직각 방법을 혼합하고 매치 할 수 있지만 대략 비슷한 방법 (A 2 개 및 B 2 개)의 여러 버전을 하나의 클래스로 채우려고한다는 것입니다. 자신이하는 일에 대해 더 많이 이야기하면 더 나은 해결책을 제안 할 수 있습니다. (당신은 또한 동일한 이름을 유지하면서 메소드의 서명을 변경하고 있습니다.)

그래도이 작업을 수행하려는 경우 다른 방법으로 name mangling을 고려해 볼 수 있습니다.

가 출력
class A(object): 
    def __init__(self): 
     self.name = "A" 
     super(A, self).__init__() 

    def Update(self): 
     print "Update A" 
     self.__PickTarget() 

    def PickTarget(self): 
     print "PickTarget A" 

    __PickTarget = PickTarget 

class B(object): 
    def __init__(self): 
     self.name = "B" 
     super(B, self).__init__() 

    def Update(self): 
     print "Update B" 
     self.__PickTarget() 

    def PickTarget(self): 
     print "PickTarget B" 

    __PickTarget = PickTarget 

class C(A, B): 
    def __init__(self): 
     super(C, self).__init__() 

    def Update(self, useA): 
     if useA: 
      A.Update(self) 
     else: 
      B.Update(self) 

: 이것은 당신이 원하는 것을 정확히 수행

>>> from mangling import A, B, C 
>>> c = C() 
>>> c.Update(useA = True) 
Update A 
PickTarget A 
>>> c.Update(useA = False) 
Update B 
PickTarget B 
+0

이런 일이 발생하는 이유에 대한 설명에 감사드립니다. 나는 C.Update()에서 A.PickTarget이나 B.PickTarget에 self.PickTarget을 설정하려고 시도했으나 "TypeError : 바인드되지 않은 메서드 인 PickTarget()은 인스턴스를 첫 번째 인수로 호출해야합니다 (아무것도 가져 오지 않았습니다)" – combatdave

+0

@combatdave, 나는 당신이 "A.PickTarget이나 B.PickTarget 중 하나에 C.Update()에서 self.PickTarget을 설정하는 것"을 의미하는지 이해하지 못합니다. 'self.PickTarget = A.PickTarget'을하고 있다는 것을 의미합니까? 그게 슈퍼 잘못이기 때문에. 'A.PickTarget'은 언 바운드 (unbound) 메쏘드이므로 이제는'self'에 더 이상 연계되어 있지 않으므로'self'를 명시 적으로 전달해야합니다. – senderle

+0

그래, 그 뜻 - 내가 잘못 생각;) 게시물 주셔서 -이 일이 무슨 일이 일어 났는지 이해하는 데 도움이되었지만 @agf에 의해 게시물 내 문제가 해결되었습니다 :) – combatdave

0

이 솔루션은 아니지만 당신이 C의 업데이트 방법을 변경하는 경우 :

def update(self, useA): 
    if useA: 
     A().update() 
    else: 
     B().update() 

이 작업을 수행 당신이 예상했던대로.

이와 같이 올바르게 작업하려면 파이썬의 메소드 해석 순서에 대해 읽어야합니다.

+1

그건 기본 클래스의 새 인스턴스를 만드는거야 유용하지 않은 Update()를 호출합니다. – combatdave

+0

그런 다음 C의 정의에서 B에 대해 스위치 A를 시도하십시오. EDIT : 또한 C.name을 인쇄하려고하면 무슨 일이 일어나는지 볼 수 있습니다. – toqueteos