Dealing with metaclass conflict with SQL Alchemy declarative base
I have a class X
which derives from a class with its own metaclass Meta
. I want to also derive X from the declarative base in SQL Alchemy. But I can't do the simple
def class MyBase(metaclass = Meta):
#...
def class X(declarative_base(), MyBase):
#...
since I would get metaclass conflict error: 'the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases'. I understand that I need to create a new metaclass that would derive from both Meta and from whatever metaclass the declarative base uses (DeclarativeMeta I think?). So is it enough to write:
def class NewMeta(Meta, DeclarativeMeta): pass
def class MyBase(metaclass = NewMeta):
#...
def class X(declarative_base(), MyBase):
#...
I tried this, and it seems to work; but I'm afraid I may have introduced some problem with this code.
I read the manual, but it's a bit too cryptic for me. What's
EDIT:
The code used for my classes is as follows:
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
开发者_StackOverflow中文版 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'
#...
Edit: Now having looked at IterRegistry
and DeclarativeMeta
, I think you're code is okay.
IterRegistry
defines __new__
and __iter__
, while DeclarativeMeta
defines __init__
and __setattr__
. Since there is no overlap, there's no direct need to call super
. Nevertheless, it would good to do so, to future-proof your code.
Do you have control over the definition of Meta
? Can you show us its definition? I don't think we can say it works or does not work unless we see the definition of Meta
.
For example, there is a potential problem if your Meta
does not call
super(Meta,cls).__init__(classname, bases, dict_)
If you run this 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
Then only the string 'Meta'
gets printed.
In other words, only Meta.__init__
gets run. DeclarativeMeta.__init__
gets skipped.
On the other hand, if you define
class Meta(type):
def __init__(cls, classname, bases, dict_):
print('Meta')
return super(Meta,cls).__init__(classname, bases, dict_)
Then both Meta.__init__
and DeclarativeMeta.__init__
gets run.
精彩评论