Der Umgang mit metaclass Konflikt mit SQL Alchemy deklarative Basis
-
26-09-2019 - |
Frage
Ich habe eine class X
, die aus einer Klasse mit einer eigenen metaclass Meta
ableitet. Ich möchte auch derive X aus der deklarativen Basis in SQL Alchemy. Aber ich kann das einfach nicht tun
def class MyBase(metaclass = Meta):
#...
def class X(declarative_base(), MyBase):
#...
da ich metaclass Konflikt Fehler bekommen würde: ‚die Metaklasse einer abgeleiteten Klasse muss eine (nicht streng) Unterklasse der metaclasses aller seiner Basen sein.‘ Ich verstehe, dass ich eine neue Metaklasse erstellen müssen, die von beiden Meta ableiten würde und von was auch immer Metaklasse die deklarative Basis verwendet (DeclarativeMeta ich denke?). So ist es genug zu schreiben:
def class NewMeta(Meta, DeclarativeMeta): pass
def class MyBase(metaclass = NewMeta):
#...
def class X(declarative_base(), MyBase):
#...
Ich habe versucht, und es scheint zu arbeiten; aber ich fürchte, ich mit diesem Code ein Problem eingeführt haben.
habe ich gelesen, das Handbuch, aber es ist ein bisschen zu kryptisch für mich. Was ist
EDIT:
Der Code für meine Klassen verwendet wird, ist wie folgt:
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'
#...
Lösung
Edit: Jetzt bei IterRegistry
und DeclarativeMeta
sah zu haben, ich glaube, du bist Code in Ordnung ist
IterRegistry
definiert __new__
und __iter__
, während DeclarativeMeta
definiert __init__
und __setattr__
. Da es keine Überlappung gibt, gibt es keine direkte Notwendigkeit Anruf super
. Trotzdem wäre es gut, dies zu tun, um zukunftssichere Code.
Haben Sie die Kontrolle über die Definition von Meta
haben? Können Sie uns ihre Definition zeigen? Ich glaube nicht, können wir sagen, es funktioniert oder nicht funktioniert, wenn wir die Definition von Meta
sehen.
Zum Beispiel gibt es ein potenzielles Problem, wenn Ihr Meta
nicht Anruf tut
super(Meta,cls).__init__(classname, bases, dict_)
Wenn Sie diesen Code ausführen
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
Dann wird nur die Zeichenfolge 'Meta'
wird gedruckt.
Mit anderen Worten wird nur Meta.__init__
laufen. DeclarativeMeta.__init__
bekommt übersprungenen.
Auf der anderen Seite, wenn Sie definieren
class Meta(type):
def __init__(cls, classname, bases, dict_):
print('Meta')
return super(Meta,cls).__init__(classname, bases, dict_)
Dann werden beide Meta.__init__
und DeclarativeMeta.__init__
wird ausgeführt werden.