2012-06-30 30 views
23

Necesito crear una clase que use una clase base diferente dependiendo de alguna condición. Con algunas clases consigo el infame:python 3: TypeError: conflicto de metaclase: la metaclase de una clase derivada debe ser una subclase (no estricta) de las metaclases de todas sus bases

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

Un ejemplo es sqlite3, aquí es un ejemplo corto incluso se puede utilizar en el intérprete:

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

¿Cómo puedo solucionar este problema?

Gracias.

+5

'sqlite3' es un módulo no una "clase". – agf

+0

@agf: Solo estaba mirando esto y me di cuenta de lo mismo cuando publicaste eso. – jdi

+0

Gracias agf, tienes razón! sqlite3.Connection hace que funcione. –

Respuesta

11

Aunque su ejemplo usando sqlite3 no es válido, porque en realidad es un módulo y no una clase, me he encontrado con este mismo problema. La clase base tiene una metaclase de un tipo diferente que la subclase, y luego verá este error.

Terminé usando una variación de this activestate snippet using noconflict.py. Desafortunadamente no es compatible con Python 3.x. Te da la idea, pero el fragmento debería ser rediseñado.

Problema fragmento de código

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 

Solución de fragmento de código

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

print C 
#<class 'C'> 

La receta código resuelve adecuadamente los metaclases para usted.

+1

Python 3 usa la 'clase C (metaclass) = MyMeta): 'sintaxis – JBernardo

+0

@JBernardo: Gracias. Comenté que no es compatible con py3. Tiene algunos otros problemas en el fragmento que hacen que no funcione tan bien. – jdi

+0

Puedes usar 'clase C (six.with_metaclass (MyMeta))' para que sea compatible con python 3.x, ¿no es así? – Tgsmith61591

12

lugar de utilizar el receipe como se ha mencionado por jdi, puede utilizar directamente:

class M_C(M_A, M_B): 
    pass 

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

Para utilizar el patrón descrito por @ Michael, pero con ambos Python 2 y 3 compatibilidad (usando la biblioteca 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

Por lo que entendí de las respuestas anteriores lo único que normalmente tenemos que hacer manualmente es:

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 

Pero podemos automatizar las dos últimas líneas ahora por:

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 

Dado que no utilizamos ningún sintaxis específica de la versión metaclase esta metaclass_resolver obras con Python 2, así como Python 3.

Cuestiones relacionadas