Gérer les conflits metaclass avec SQL Alchemy de base déclarative
-
26-09-2019 - |
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'
#...
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.