2011-07-02 5 views
37

나는 그와 아무 관계가 없어도 메타 클래스 지옥을 발견했습니다.트리플 상속으로 인해 메타 클래스 충돌이 발생합니다 ... 간혹

저는 PySide를 사용하여 Qt4에 앱을 작성하고 있습니다. 이벤트 중심 부분을 Qt Designer 파일에서 생성 된 UI 정의와 분리하려고합니다. 따라서 "컨트롤러"클래스를 만들지 만, 내 인생을 편하게하기 위해 어쨌든 여러 개의 상속 클래스를 상속합니다. 예 :

이것은 예상대로 작동합니다. 또한 (QDialog, Ui_Dialog, BaseController)의 상속을가집니다.

TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

명확한 설명 : QObject에서 모두 QMainWindowQDialog 상속 내가 BaseController를 서브 클래스에서 상속하려고 할 때 (BaseController 대신에) 서브 클래스가, 나 오류가 발생했다. BaseController도 Qt 이벤트 시스템 특성 때문에 그것으로부터 상속되어야합니다. Ui_ 클래스는 단순한 Python 객체 클래스에서만 상속받습니다. 나는 해결책을 찾았지만, 그들 모두는 의도적으로 메타 클래스를 사용하는 경우를 포함한다. 그래서 나는 끔찍한 잘못을 저 지르게 될 것임에 틀림 없다.

편집 : 내 설명은 그래프를 추가하면 더 명확해질 수 있습니다.

근무 예 :

QObject 
|  \___________________ 
|   object  | 
QMainWindow  |   BaseController 
|  /---Ui_MainWindow | 
|  |     MainController 
MainWindow-----------------/ 

또 다른 작업 예 :

QObject 
|  \___________________ 
|   object  | 
QDialog   |   BaseController 
|  /---Ui_OtherWindow | 
|  |     | 
OtherWindow----------------/ 

작동하지 예 :

QObject 
|  \___________________ 
|   object  | 
QDialog   |   BaseController 
|  /---Ui_OtherWindow | 
|  |     OtherController 
OtherWindow----------------/ 
+0

저는 파이썬 메타 클래스에별로 좋지 않습니다.하지만 문제는 'MainWindow' 클래스 정의 내에서 부모 클래스를 정렬하는 것과 관련이 있다고 생각합니다. 그냥 추측. – Tony

+0

나에게 가장 혼란스러운 점은 QDialog, Ui_Dialog 및 컨트롤러를 QObject에서 상속 한 클래스, QObject에서 상속 한 클래스에서 상속 한 클래스, QObject에서 상속 한 클래스와 같은 시퀀스에 넣으면서 MainWindow가 작동한다는 것입니다. – Red

+0

파이썬에 대해서는 잘 모르지만 C++/Qt에서는 QObject의 다중 상속이 엄격히 금지되어 있습니다. 나는 당신이 동일한 문제를 겪고 있는지 궁금해하고, 당신의 경우 중 일부에서 일하는 것 같습니다. –

답변

32

오류 메시지가 당신이 당신의 계층 구조에서 두 개의 충돌 메타 클래스 어딘가에 있음을 나타냅니다 . 충돌이있는 곳을 파악하려면 각 클래스와 QT 수업을 검토해야합니다. 파이썬이 사용하는 메타 클래스 알고하지 않기 때문에,

class MetaA(type): 
    pass 
class MetaB(type): 
    pass 
class A: 
    __metaclass__ = MetaA 
class B: 
    __metaclass__ = MetaB 

우리는 직접 해당 클래스의 모두를 서브 클래 싱 할 수 없습니다 : 여기

이 같은 상황을 설정 몇 가지 간단한 예제 코드입니다

>>> class Broken(A, B): pass 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: Error when calling the metaclass bases 
    metaclass conflict: the metaclass of a derived class must be a (non-strict) 
    subclass of the metaclasses of all its bases 

오류가 우리에게 말하고자하는 것은 기본 클래스의 모든 메타 클래스의 하위 클래스 인 세 번째 메타 클래스를 도입하여 두 메타 클래스 간의 충돌을 해결해야한다는 것입니다.

은 그 어떤 오류 메시지 자체보다 명확이다 모르겠지만, 기본적으로,이 작업을 수행하여 문제를 해결할 :

class MetaAB(MetaA, MetaB): 
    pass 

class Fixed(A, B): 
    __metaclass__ = MetaAB 

이 코드는 이제 컴파일하고 제대로 실행됩니다. 물론 실제 상황에서는 갈등 해결 메타 클래스가 부모 메타 클래스 동작을 채택해야할지 결정해야하며, 이는 응용 프로그램 요구 사항에서 스스로 알아야 할 것입니다.

상속 된 클래스는 중 하나만 두 개의 메타 클래스 중 하나임을 명심하십시오.__init__ 메쏘드는 때로는 모든 작업을 수행하기 때문에, 많은 경우에 두 가지 모두를 호출하는 __init__을 추가해야 할 것입니다. 좋은, 현대 메타 클래스-구현 위생 가정

class CooperativeMeta(type): 
    def __new__(cls, name, bases, members): 
     #collect up the metaclasses 
     metas = [type(base) for base in bases] 

     # prune repeated or conflicting entries 
     metas = [meta for index, meta in enumerate(metas) 
      if not [later for later in metas[index+1:] 
       if issubclass(later, meta)]] 

     # whip up the actual combined meta class derive off all of these 
     meta = type(name, tuple(metas), dict(combined_metas = metas)) 

     # make the actual object 
     return meta(name, bases, members) 

    def __init__(self, name, bases, members): 
     for meta in self.combined_metas: 
      meta.__init__(self, name, bases, members) 

(서브 클래스 type를 메타 클래스, 그리고이 완료됩니다 __init__ 수행 할 수 있습니다 무엇이든)이 많은 메타 클래스가 함께 얻을 수 있습니다 :

+0

Definetly 정답입니다. 이해해 주셔서 감사합니다. 프로그래밍 방법론 (즉, 클래스를 상속하는 것이 아니라 적응하는 것)에 대해 조금 배웠지 만, 내가 필요한 곳에 메타 클래스를 다룰 때이 지식을 갖는 것은 여전히 ​​유용합니다. 다시 한번 감사드립니다. – Red

7

우리는 같은 것을 사용합니다.

어쨌든 __new__에서 대부분의 작업을 수행하는 메타 클래스는 어쨌든 결합하기가 어려울 것입니다. 클래스가 다중 상속의 첫 번째 요소인지 확인하여 여기에있는 하나를 몰래 빠져 나올 수 있습니다.

이를 사용하려면, 당신은 단지 선언 : 다른 메타 클래스는 함께 모여 그 클래스에 대한

__metaclass__ = CooperativeMeta 

. 예를 들어이 경우

는 :

class A: 
    __metaclass__ = MetaA 
class B: 
    __metaclass__ = MetaB 
class Fixed(A, B): 
    __metaclass__ = CooperativeMeta 

이것은 단지 컴파일러 닥쳐 그들을 함께 상속 다른 MetaA 및 MetaB에 대한 전면적 제대로 작동하려면 많은 시간이 가능성이 높습니다.

주석이 코드를 설명하기를 바랍니다. 단지 하나의 까다로운 행이 있습니다. 다른 곳에서 상속받은 __metaclass__에 대한 중복 호출을 제거하고 명시 적 메타 클래스가없는 클래스가 다른 클래스와 잘 작동하도록 허용하는 것입니다. 그것이 과도하게 보인다면, 코드를 생략하고 기본 클래스를주의 깊게 순서대로 정렬 할 수 있습니다.

이렇게하면 솔루션이 세 줄로되어 매우 명확합니다.

관련 문제