Python - dynamische Mehrfachvererbung

8

Ich suche Ratschläge zum Design meines Codes.

Einführung

Ich habe mehrere Klassen, jede repräsentiert einen Dateityp, zB: MediaImageFile, MediaAudioFile und generische (und auch Basisklasse) MediaGenericFile.

Jede Datei hat zwei Varianten: Master und Version, also habe ich diese Klassen erstellt, um ihr spezifisches Verhalten zu definieren. BEARBEITEN: Version stellt die geänderte / beschnittene / abgeschnittene / etc-Variante der Master-Datei dar. Es wird hauptsächlich für Vorschauen verwendet.

BEARBEITEN: Der Grund, warum ich es dynamisch machen möchte ist, dass diese App wiederverwendbar sein soll (es ist Django-App) und daher sollte es einfach sein, andere MediaGenericFile-Unterklassen ohne zu implementieren Ändern des ursprünglichen Codes.

Was ich machen möchte

  1. Zunächst sollte der Benutzer in der Lage sein, eigene MediaGenericFile-Unterklassen zu registrieren, ohne den Originalcode zu beeinflussen.

  2. Ob Datei ist Version oder Master ist leicht (eine Regexp) aus dem Dateinamen erkennbar.

    %Vor%
  3. Master / Version-Klassen verwenden einige Methoden / Eigenschaften von MediaGenericFile, wie Dateiname (Sie müssen den Dateinamen kennen, um eine neue Version zu generieren).

  4. MediaGenericFile erweitert LazyFile, das nur ein faules Dateiobjekt ist.

Jetzt muss ich es zusammenfügen ...

Verwendetes Design

Bevor ich mit der Programmierung der 'versions' -Funktion begann, hatte ich die Factory-Klasse MediaFile, die die entsprechende Dateitypklasse entsprechend der Erweiterung zurückgibt:

%Vor%

Die Klassen Master und Version definieren neue Methoden, die Methoden und Attribute von MediaGenericFile usw. verwenden.

Ansatz 1

Ein Ansatz ist der dynamisch neue Typ, der Master (oder Version) und MediaGenericFile (oder Unterklasse) erbt.

%Vor%

Ansatz 2

Der zweite Ansatz ist die create Methode 'contribute_to_instance' in Master / Version und ruft sie nach dem Erstellen von new_class auf, aber das ist schwieriger, als ich dachte:

%Vor%

Dies funktioniert jedoch nicht. Es gibt immer noch Probleme beim Aufruf der Methoden von Master / Version.

Fragen

Was wäre ein guter Weg, diese Mehrfachvererbung zu implementieren?

Wie heißt das Problem? :) Ich habe versucht, einige Lösungen zu finden, aber ich weiß einfach nicht, wie ich dieses Problem benennen soll.

Vielen Dank im Voraus!

Hinweis zu den Antworten

ad larsmans

Vergleich und Instanzprüfung wären für meinen Fall kein Problem, denn:

  1. Vergleiche werden trotzdem neu definiert

    %Vor%
  2. Ich muss nie isinstance (MediaGenericFileVersion, instance) prüfen. Ich verwende isinstance (MediaGenericFile, Instanz) und isinstance (Version, Instanz) und beide funktionieren wie erwartet.

Trotzdem klingt das Erstellen eines neuen Typs pro Instanz für mich als erheblicher Defekt.

Nun, ich könnte beide Variationen dynamisch in der Metaklasse erstellen und sie dann verwenden, etwa wie folgt:

%Vor%

Und dann:

%Vor%

Endgültige Lösung

Schließlich ist das Designmuster eine Fabrikklasse. MediaGenericFile-Unterklassen sind statisch typisiert, Benutzer können ihre eigenen implementieren und registrieren. Master / Version-Varianten werden dynamisch (aus mehreren Mixins zusammengefügt) in der Metaklasse erstellt und im 'Cache' gespeichert, um Gefahren zu vermeiden, die von larsmans erwähnt werden.

Danke allen für ihre Vorschläge. Endlich verstehe ich das Metaklassen-Konzept. Nun, zumindest denke ich, dass ich es verstehe. Drücken Sie den Ursprungs-Master ...

    
Tomáš Ehrlich 21.07.2012, 11:03
quelle

5 Antworten

0

Sie müssen für jede Instanz keine neue Klasse erstellen. Erstellen Sie die neuen Klassen nicht in __new__ und erstellen Sie sie in __metaclass__ . Definieren Sie eine Metaklasse in der Basis oder im base_module. Die beiden "varianten" -Unterklassen können einfach als Klassenattribute ihres entsprechenden Elternelements gespeichert werden, und dann schaut __new__ nur den Dateinamen nach eigenen Regeln an und entscheidet, welche Unterklasse zurückgegeben wird.

Achten Sie auf __new__ , das eine andere als die während des Konstruktoraufrufs "nominierte" Klasse zurückgibt. Möglicherweise müssen Sie Schritte ergreifen, um __init__ von __new__ aufzurufen.

Unterklassen müssen entweder:

  1. "registrieren" sich selbst mit einer Fabrik oder einem Elternteil, um gefunden zu werden
  2. Seien Sie import ed und lassen Sie die Eltern oder das Werk durch eine rekursive Suche nach cls.__subclasses suchen (dies muss möglicherweise einmal pro Erstellung passieren, aber das ist wahrscheinlich kein Problem für die Dateiverwaltung)
  3. gefunden durch die Verwendung von "setuptools" entry_points Typ-Tools, aber das erfordert mehr Aufwand und Koordination durch den Benutzer
Phil Cooper 21.07.2012, 13:50
quelle
4

Ich bin mir nicht sicher, wie dynamisch es sein soll, aber ein "Fabrikmuster" (hier unter Verwendung einer Klassenfabrik) ist ziemlich lesbar und verständlich und kann tun, was Sie wollen. Dies könnte als Basis dienen ... MediaFactory könnte schlauer sein, und Sie könnten mehrere andere Klassen registrieren, anstatt MediaFactoryMaster etc ... hart zu codieren.

%Vor%

andere mögliche MediaFactory.make

%Vor%     
Jon Clements 21.07.2012 11:48
quelle
3

Ich würde sicherlich von dem ersten Ansatz abraten, Klassen in __new__ zu konstruieren. Das Problem dabei ist, dass Sie einen neuen Typ pro Instanz erstellen, was Overhead und Schlimmeres verursacht und dazu führt, dass Typvergleiche fehlschlagen:

%Vor%

Dies verletzt das Prinzip der geringsten Überraschung, denn die Klassen scheinen völlig identisch zu sein:

%Vor%

Wenn Sie die Klassen auf Modulebene außerhalb von MediaFile :

konstruieren, können Sie Approach 1 jedoch richtig arbeiten lassen %Vor%

Dann, in MediaFile.__new__ , suchen Sie die gewünschte Klasse nach Namen in classes . (Alternativ können Sie die neu erstellten Klassen auf dem Modul statt in dict setzen.)

    
Fred Foo 21.07.2012 11:16
quelle
2

Wie kommt es, dass Sie keine Vererbung verwenden, aber mit __new__ ?

herumspielen? %Vor%

Es ist nicht klar, warum du das tust. Ich denke, hier ist ein komischer Code-Geruch. Ich würde weniger Klassen mit einer konsistenten Schnittstelle ( Duck-Typing ) als ein Dutzend Klassen empfehlen und isinstance überprüft den gesamten Code, damit alles funktioniert.

Vielleicht können Sie Ihre Frage mit dem, was Sie in Ihrem Code tun möchten aktualisieren und Leute können helfen, entweder das echte Muster zu identifizieren oder eine idiomatische Lösung vorschlagen.

    
stderr 21.07.2012 11:42
quelle
0

Die OOD-Frage, die Sie stellen sollten, lautet: "Teilen die verschiedenen Klassen meiner vorgeschlagenen Vererbung überhaupt Eigenschaften ?

?

Der Zweck der Vererbung ist es, gemeinsame Daten oder Methoden zu teilen, die die Instanzen natürlich gemeinsam haben. Abgesehen von beiden Dateien, was haben Bilddateien und Audiodateien gemeinsam? Wenn Sie Ihre Metaphern wirklich strecken wollen, könnten Sie vielleicht AudioFile.view() haben, was zum Beispiel eine Visualisierung der Leistungsspektren der Audiodaten darstellen könnte, aber ImageFile.listen() macht noch weniger Sinn.

Ich denke, dass Ihre Frage dieses sprachunabhängige konzeptuelle Problem zugunsten der Python-abhängigen Mechanik einer Objektfabrik behandelt. Ich denke nicht, dass Sie hier einen ordnungsgemäßen Vererbungsfall haben, oder Sie haben nicht erklärt, welche gemeinsamen Merkmale Ihre Media-Objekte teilen müssen.

    
msw 21.07.2012 11:42
quelle