2012-06-30 14 views
23

Potrzebuję utworzyć klasę, która używa innej klasy bazowej w zależności od pewnych warunków. W niektórych klasach I dostać niesławny:python 3: TypeError: konflikt metaklowy: metaclass klasy pochodnej musi być (nie ścisłą) podklasą metaclasses wszystkich jej baz

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

Jednym z przykładów jest sqlite3, oto krótki przykład można nawet użyć w tłumacza:

>>> import sqlite3 
>>> x = type('x', (sqlite3,), {}) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases 
>>> 

Jak mogę rozwiązać ten problem?

Dzięki.

+5

'sqlite3' to moduł, a nie "klasa". – agf

+0

@agf: Właśnie na to patrzyłem i zrozumiałem to samo, kiedy to opublikowałeś. – jdi

+0

Dzięki, agf, masz rację! sqlite3.Connection sprawia, że ​​działa. –

Odpowiedz

11

Nawet jeśli Twój przykład z użyciem sqlite3 jest nieprawidłowy, ponieważ jest to moduł, a nie klasa, napotkałem ten sam problem. Klasa bazowa ma metaklas innego typu niż podklasa, a następnie widzisz ten błąd.

Skończyło się na użyciu wersji this activestate snippet using noconflict.py. Nie jest to kompatybilne z Pythonem 3.x. Daje ci to pomysł, ale fragment musiałby zostać przerobiony.

Problem snippet

class M_A(type): 
    pass 
class M_B(type): 
    pass 
class A(object): 
    __metaclass__=M_A 
class B(object): 
    __metaclass__=M_B 
class C(A,B): 
    pass 

#Traceback (most recent call last): 
# File "<stdin>", line 1, in ? 
#TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass #of the metaclasses of all its bases 

Rozwiązanie snippet

from noconflict import classmaker 
class C(A,B): 
    __metaclass__=classmaker() 

print C 
#<class 'C'> 

Kod przepis prawidłowo rozwiązuje metaclasses dla Ciebie.

+1

Python 3 używa 'klasy C (metaklasa = MyMeta): 'składnia – JBernardo

+0

@JBernardo: Dzięki. Skomentowałem, że nie jest to py3 compat. W fragmencie kodu występuje kilka innych problemów, które sprawiają, że nie działa tak dobrze. – jdi

+0

Możesz użyć 'klasy C (six.with_metaclass (MyMeta))' aby uczynić ją zgodną z Pythonem 3.x, czyż nie? – Tgsmith61591

12

Zamiast receipe jak wspomniano przez JDI można bezpośrednio użyć:

class M_C(M_A, M_B): 
    pass 

class C(A, B): 
    __metaclass__ = M_C 
6

Aby użyć wzoru opisanego przez @michael, ale zarówno Python 2 i 3 zgodności (z użyciem biblioteki six):

from six import with_metaclass 

class M_C(M_A, M_B): 
    pass 

class C(with_metaclass(M_C, A, B)): 
    # implement your class here 
1

o ile zrozumiałem z poprzednich odpowiedzi tylko myśleć, że zwykle trzeba zrobić ręcznie jest:

class M_A(type): pass 
class M_B(type): pass 
class A(metaclass=M_A): pass 
class B(metaclass=M_B): pass 

class M_C(M_A, M_B): pass 
class C:(A, B, metaclass=M_C): pass 

Ale możemy zautomatyzować dwie ostatnie linie teraz przez:

def metaclass_resolver(*classes): 
    metaclass = tuple(set(type(cls) for cls in classes)) 
    metaclass = metaclass[0] if len(metaclass)==1 \ 
       else type("_".join(mcls.__name__ for mcls in metaclass), metaclass, {}) # class M_C 
    return metaclass("_".join(cls.__name__ for cls in classes), classes, {})    # class C 

class C(metaclass_resolver(A, B)): pass 

Ponieważ nie wykorzystać błędy składniowe metaklasa wersji specyficznych to metaclass_resolver prace z Python 2, jak również Python 3.