2010-09-02 3 views
4

J'ai un class X qui dérive d'une classe avec sa propre métaclasse Meta. Je veux aussi dériver X de la base déclarative dans SQL Alchemy. Mais je ne peux pas faire simpleTraiter le conflit de métaclasses avec la base déclarative SQL Alchemy

def class MyBase(metaclass = Meta): 
    #... 

def class X(declarative_base(), MyBase): 
    #... 

depuis que je recevrais erreur métaclasse conflit: la métaclasse d'une classe dérivée doit être un (non stricte) sous-classe des métaclasses de toutes ses bases. Je comprends que j'ai besoin de créer une nouvelle métaclasse qui dériverait à la fois de Meta et de la métaclasse que la base déclarative utilise (DeclarativeMeta je pense?). Alors, est-ce suffisant pour écrire:

def class NewMeta(Meta, DeclarativeMeta): pass 
def class MyBase(metaclass = NewMeta): 
    #... 
def class X(declarative_base(), MyBase): 
    #... 

J'ai essayé, et il semble travailler; mais j'ai peur d'avoir introduit un problème avec ce code. Je lis le manuel, mais c'est un peu trop énigmatique pour moi. Qu'est-ce

EDIT:

Le code utilisé pour mes classes est la suivante:

class IterRegistry(type): 
    def __new__(cls, name, bases, attr): 
     attr['_registry'] = {} 
     attr['_frozen'] = False 
     print(name, bases) 
     print(type(cls)) 
     return type.__new__(cls, name, bases, attr) 
    def __iter__(cls): 
     return iter(cls._registry.values()) 

class SQLEnumMeta(IterRegistry, DeclarativeMeta): pass 

class EnumType(metaclass = IterRegistry): 
    def __init__(self, token): 
     if hasattr(self, 'token'): 
      return 
     self.token = token 
     self.id = len(type(self)._registry) 
     type(self)._registry[token] = self 

    def __new__(cls, token): 
     if token in cls._registry: 
      return cls._registry[token] 
     else: 
      if cls._frozen: 
       raise TypeError('No more instances allowed') 
      else: 
       return object.__new__(cls) 

    @classmethod 
    def freeze(cls): 
     cls._frozen = True 

    def __repr__(self): 
     return self.token 

    @classmethod 
    def instance(cls, token): 
     return cls._registry[token] 

class C1(Base, EnumType, metaclass = SQLEnumMeta): 
    __tablename__ = 'c1' 
    #... 

Répondre

3

Edit: Maintenant, après avoir regardé IterRegistry et DeclarativeMeta, je pense que vous êtes le code est correct.

IterRegistry définit __new__ et __iter__, tandis que DeclarativeMeta définit __init__ et __setattr__. Comme il n'y a pas de chevauchement, il n'y a pas besoin d'appeler directement super. Néanmoins, il serait bon de le faire, pour l'avenir de votre code. Vous avez le contrôle sur la définition de Meta? Pouvez-vous nous montrer sa définition? Je ne pense pas que nous pouvons dire que cela fonctionne ou ne fonctionne pas, sauf si nous voyons la définition de Meta.

Par exemple, il y a un problème potentiel si votre Meta ne remet pas super(Meta,cls).__init__(classname, bases, dict_)

Si vous exécutez ce code

class DeclarativeMeta(type): 
    def __init__(cls, classname, bases, dict_): 
     print('DeclarativeMeta') 
     # if '_decl_class_registry' in cls.__dict__: 
     #  return type.__init__(cls, classname, bases, dict_)  
     # _as_declarative(cls, classname, dict_) 
     return type.__init__(cls, classname, bases, dict_) 

class Meta(type): 
    def __init__(cls, classname, bases, dict_): 
     print('Meta') 
     return type.__init__(cls, classname, bases, dict_) 

class NewMeta(Meta,DeclarativeMeta): pass 

class MyBase(object): 
    __metaclass__ = NewMeta 
    pass 

Alors que la chaîne 'Meta' est imprimé. En d'autres termes, seul Meta.__init__ est exécuté. DeclarativeMeta.__init__ est ignoré.

D'autre part, si vous définissez

class Meta(type): 
    def __init__(cls, classname, bases, dict_): 
     print('Meta') 
     return super(Meta,cls).__init__(classname, bases, dict_) 

Puis à la fois Meta.__init__ et DeclarativeMeta.__init__ se fasse automatiquement.

+0

Merci. J'ai négligé ce problème précis. J'ai ajouté les détails dans ma question, mais il semble que les choses deviennent trop compliquées à ce stade .. – max

+0

Merci. Je vais remplacer 'return type .__ new __ (cls, nom, bases, attr)' par 'return super() .__ nouveau __ (cls, nom, bases, attr)'. Bien qu'il ne soit pas infaillible, au moins c'est un peu mieux que d'espérer qu'il n'y a pas de __new__ dans la base déclarative.Certes, tout cet héritage multiple est très dangereux, puisque personne ne fait de contrat précis sur le comportement ... – max

+0

L'héritage multiple est généralement correct (en Python). Mais l'héritage multiple des classes qui sont des instances de métaclasses différentes est difficile, avec de bonnes raisons (c'est pourquoi il existe une règle sur la fusion explicite de métaclasses). Nous pouvons comprendre ce qui signifierait que quelque chose soit un cheval et un oiseau (même si nous n'en avons jamais vu), mais il est beaucoup plus difficile de voir ce que «être un cheval et une bonne idée» pourrait signifier. :-RÉ – Veky

Questions connexes