Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Die Situation: Ich habe eine Klasse, die beim __init__ feststellt, welche andere Klasse subklassiert werden soll. So wie ich es versucht habe, geht es natürlich nicht. Aber wie dann?
class Database(object):
def __init__(self, engine):
if engine == 'postgresql':
self = MySQL()
if engine == 'mysql':
self = PostgreSQL()
class PostgreSQL(object):
def select(self):
print 'select for pg'
class MySQL(object):
def select(self):
print 'select for mysql'
db = Database(engine='mysql')
db.select()
Du kannst natuerlich auch bei deiner `if`-Kaskade bleiben.
Darauf bin ich auch schon gekommen. Allerdings muß die Klasse "Database" an anderer Stelle wieder abgeleitet werden, deshalb geht das mit der Factory-Funktion leider nicht... Ich kann ja schlecht versuchen, eine Klasse von einer Funktion abzuleiten...
Red Rooster hat geschrieben:
Danke für die prompte Antwort! Ja, ich glaube das ist genau was ich gesucht habe. Kann gar nicht glauben, daß es so einfach ist
Ohne den genauen Hintergrund zu kennen würde ich ja sagen, Du suchst einen ORM. Da böte sich im Python Umfeld SQLAlchemy an - es sein denn, Du willst unbedingt Deinen eigenen schreiben...
Red Rooster hat geschrieben:
Danke für die prompte Antwort! Ja, ich glaube das ist genau was ich gesucht habe. Kann gar nicht glauben, daß es so einfach ist
Ohne den genauen Hintergrund zu kennen würde ich ja sagen, Du suchst einen ORM. Da böte sich im Python Umfeld SQLAlchemy an - es sein denn, Du willst unbedingt Deinen eigenen schreiben...
Danke für den Tip, ich kenne SQLAlchemy. Ich brauche für mein Projekt aber einen völlig anderen Denkansatz
class Database(object):
def __new__(cls, engine):
if engine == 'mysql':
cls = MySQL
elif engine == 'pg':
cls = PostgreSQL
else:
raise Exception('Unsupported engine')
return object.__new__(cls)
class PostgreSQL(Database):
def select(self):
print('select for pg')
class MySQL(Database):
def select(self):
print('select for mysql')
db = Database(engine='mysql')
db.select()
db = Database(engine='pg')
db.select()
db = Database(engine='oracle')
db.select()
Danke für die prompte Antwort! Ja, ich glaube das ist genau was ich gesucht habe. Kann gar nicht glauben, daß es so einfach ist
Verdammt. Leider scheint es doch nicht so einfach zu sein! Wenn ich jetzt die Klasse "Database" nehme und ableite, dann wird alles in der abgeleiteten Klasse überschrieben (und außerdem wird __init__ nicht mehr ausgeführt). Es sollte aber umgekehrt sein, daß die ableitende Klasse den Inhalt der Elternklasse überschreibt (wie es eigentlich üblich ist).
Du musst in der __init__-Methode von my_db auch die __init__ von Database aufrufen.
Edit: In diesem Fall liegt es natürlich daran, dass du für die Engine "pg" eine Instanz von PostgreSQL erstellst, da wird natürlich die init von my_db aufgerufen. Das passiert halt, wenn man ein schlechtes Design wählt Spezialisierungen sollten immer in der abgeleitetend Klasse stehen, die Elternklasse sollte gar nicht wissen, welche verschiedenen Ableitungen es gibt. Mit einer abstrakten Methode zur Initialisierung, welche durch die Kinder bereitgestellt und in der __init__ von Database ausgeführt wird, sparst du dir eine ganze Menge Ärger.
EyDu hat geschrieben:Du musst in der __init__-Methode von my_db auch die __init__ von Database aufrufen.
Edit: In diesem Fall liegt es natürlich daran, dass du für die Engine "pg" eine Instanz von PostgreSQL erstellst, da wird natürlich die init von my_db aufgerufen.
Die __init__ von my_db wird nicht aufgerufen, weil die Klasse my_db von der Klasse PostgreSQL überschrieben wird!
@Red Rooster: Du versuchst da etwas was IMHO mit einer hierarchischen Klassenhierarchie so nicht geht. Was Du willst ist ja nicht von `Database` abzuleiten, sondern von mehreren Unterklassen davon quasi gleichzeitig abzuleiten. Das ist nicht vorgesehen.
Du solltest besser aus Deiner `MyDB` einen Proxy zu einer DB-Klasse machen. Dann ist es zum Beispiel auch ganz einfach die `select()` von der konkreten Datanbankklasse aus der überschriebenen `select()` heraus aufzurufen. Wie hättest Du Dir das bei Deinem Ansatz denn vorgestellt?
BlackJack hat geschrieben:@Red Rooster: Du versuchst da etwas was IMHO mit einer hierarchischen Klassenhierarchie so nicht geht. Was Du willst ist ja nicht von `Database` abzuleiten, sondern von mehreren Unterklassen davon quasi gleichzeitig abzuleiten. Das ist nicht vorgesehen.
Du solltest besser aus Deiner `MyDB` einen Proxy zu einer DB-Klasse machen. Dann ist es zum Beispiel auch ganz einfach die `select()` von der konkreten Datanbankklasse aus der überschriebenen `select()` heraus aufzurufen. Wie hättest Du Dir das bei Deinem Ansatz denn vorgestellt?
Die Hauptklasse "Database" soll sich zunächst selbst überschreiben (mit einer der Adapter-Klassen MySQL und PostgreSQL). Das funktioniert ja auch mit dem bestehenden Konstrukt. Ich müßte aber die Klasse "Database" einfach wie eine ganz normale Klasse ableiten können, ohne daß an dieser Stelle meine Unterklasse (my_db) überschrieben wird.
@Red Rooster: Mir ist schon klar was Du willst, das ist aber nicht so einfach möglich, weil es eben der vorgesehenen Klassenhierarchie zuwider läuft.
Den Begriff „überschreiben” solltest Du hier vielleicht nicht verwenden, weil da nichts dergleichen passiert. Du rufst den Konstruktor von `my_db` auf, was der geerbte von `Database` ist. Und dort erstellst und gibst Du ein Exemplar der beiden anderen Unterklassen, abhängig vom Argument zurück. Von `my_db` wird niemals ein Exemplar erstellt.
Und ich sage es noch einmal: Lass diese Magie bleiben und schreibe einen Proxy statt das über Vererbung lösen zu wollen. Das ist eine saubere, einfache Lösung von der man auch die Klassenhierarchie zeichnen kann ohne Probleme zu bekommen.
BlackJack hat geschrieben:@Red Rooster: Mir ist schon klar was Du willst, das ist aber nicht so einfach möglich, weil es eben der vorgesehenen Klassenhierarchie zuwider läuft.
Den Begriff „überschreiben” solltest Du hier vielleicht nicht verwenden, weil da nichts dergleichen passiert. Du rufst den Konstruktor von `my_db` auf, was der geerbte von `Database` ist. Und dort erstellst und gibst Du ein Exemplar der beiden anderen Unterklassen, abhängig vom Argument zurück. Von `my_db` wird niemals ein Exemplar erstellt.
Und ich sage es noch einmal: Lass diese Magie bleiben und schreibe einen Proxy statt das über Vererbung lösen zu wollen. Das ist eine saubere, einfache Lösung von der man auch die Klassenhierarchie zeichnen kann ohne Probleme zu bekommen.
Ich kann keinen Proxy aus der my_db machen, weil sie in einer bestehenden Applikation an zig Stellen verwendet wird. BlackMagic ist hier äußerst angebracht, weil alle anderen Lösungen einen absolut unrealistischen Aufwand erfordern würden. Wenn das Problem nicht äußerst kompliziert wäre, dann würde ich hier gar nicht posten!
@Red Rooster: Ich sehe nicht warum Du keinen Proxy daraus machen können solltest. Das Entwurfsmuster zeichnet sich ja dadurch aus, dass es nach aussen wie der Datentyp verhält, für den es als Stellvertreter und Vermittler steht. Gerade bei dynamischer Typisierung geht so etwas doch sehr problemlos durch. Das hier ist deutlich weniger magisch als was Du da versuchst:
BlackJack hat geschrieben:@Red Rooster: Ich sehe nicht warum Du keinen Proxy daraus machen können solltest. Das Entwurfsmuster zeichnet sich ja dadurch aus, dass es nach aussen wie der Datentyp verhält, für den es als Stellvertreter und Vermittler steht. Gerade bei dynamischer Typisierung geht so etwas doch sehr problemlos durch. Das hier ist deutlich weniger magisch als was Du da versuchst:
Zusätzlich könnte man `Database` hier auch durch eine Fabrikfunktion ersetzen um das `__new__()` los zu werden.
Danke! Jetzt sehe ich, was Du meinst. Dann sollte ich wohl gleich aus Database eine Proxy-Klasse machen, das __new__ rausschmeißen und dann kann ich auch MyDB wieder ohne Problem von Database ableiten!
Ich hatte gar nicht gewußt, daß man __getattr__ auf eine beliebige Klasse umleiten kann!
BlackJack hat geschrieben:@Red Rooster: Ich sehe nicht warum Du keinen Proxy daraus machen können solltest. Das Entwurfsmuster zeichnet sich ja dadurch aus, dass es nach aussen wie der Datentyp verhält, für den es als Stellvertreter und Vermittler steht. Gerade bei dynamischer Typisierung geht so etwas doch sehr problemlos durch. Das hier ist deutlich weniger magisch als was Du da versuchst:
Zusätzlich könnte man `Database` hier auch durch eine Fabrikfunktion ersetzen um das `__new__()` los zu werden.
Danke! Jetzt sehe ich, was Du meinst. Dann sollte ich wohl gleich aus Database eine Proxy-Klasse machen, das __new__ rausschmeißen und dann kann ich auch MyDB wieder ohne Problem von Database ableiten!
Ich hatte gar nicht gewußt, daß man __getattr__ auf eine beliebige Klasse umleiten kann!
Zuletzt geändert von Red Rooster am Freitag 9. November 2012, 17:55, insgesamt 1-mal geändert.
Sagt doch die Fehlermeldung aus: self.db existiert in Database.select nicht, also wird __getattr__ aufgerufen. Das versucht auf dem Objekt self.db, welches es natürlich nicht gibt, etwas zu machen. Also wird __getattr__ aufgerufen um self.db zu finden. Den Rest kannst du dir ja vorstellen.
EyDu hat geschrieben:Sagt doch die Fehlermeldung aus: self.db existiert in Database.select nicht, also wird __getattr__ aufgerufen. Das versucht auf dem Objekt self.db, welches es natürlich nicht gibt, etwas zu machen. Also wird __getattr__ aufgerufen um self.db zu finden. Den Rest kannst du dir ja vorstellen.
Danke! Jetzt seh' ich den Wald vor lauter Bäumen nicht mehr... Ich mach' Feierabend für heute.