Question

J'ai un class X qui dérive d'une classe avec sa propre Meta metaclass. Je veux aussi tirer X de la base déclarative dans SQL Alchemy. Mais je ne peux pas faire simple

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

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

depuis que je recevrais métaclasse erreur de 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 je dois créer une nouvelle métaclasse qui proviendraient de deux Meta et de tout ce qui métaclasse les utilisations de base déclarative (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 semble au travail; mais je crains que je vous ai présenté un problème avec ce code.

J'ai lu le manuel, mais il est un peu trop cryptique pour moi. Ce qui est

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'
    #...
Était-ce utile?

La solution

Edit: Maintenant, après avoir regardé IterRegistry et DeclarativeMeta, je pense que tu as 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, à l'épreuve du code.


Avez-vous un contrôle sur la définition de Meta? Pouvez-vous nous montrer sa définition? Je ne pense pas qu'on puisse dire que cela fonctionne ou ne fonctionne pas à moins que nous voyons la définition de Meta.

Par exemple, il y a un problème potentiel si votre Meta ne appel 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, seuls Meta.__init__ s'exécuter. DeclarativeMeta.__init__ obtient sautée.

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

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top