34 Stimmen

Unterklassifizierung von dict: sollte dict.__init__() aufgerufen werden?

Es handelt sich um eine doppelte Frage, die einen theoretischen und einen praktischen Teil hat:

Bei der Unterklassifizierung von dict:

class ImageDB(dict):
    def __init__(self, directory):
        dict.__init__(self)  # Necessary?? 
        ...

sollte dict.__init__(self) aufgerufen werden, nur als "Sicherheits"-Maßnahme (z.B. für den Fall, dass es einige nicht-triviale Implementierungsdetails gibt, die von Bedeutung sind)? Besteht die Gefahr, dass der Code mit einer zukünftigen Version von Python nicht mehr funktioniert, wenn dict.__init__() es no angerufen? Ich suche nach einem grundsätzlichen Grund, das eine oder das andere zu tun, hier (praktisch, indem ich dict.__init__() sicher ist).

Meine Vermutung ist, dass wenn ImageDB.__init__(self, directory) aufgerufen wird, ist self bereits ein neues, leeres dict-Objekt, und es besteht daher keine Notwendigkeit, die dict.__init__ (Ich möchte, dass das Diktat anfangs leer ist). Ist das richtig?

bearbeiten :

Die praktische Frage, die sich hinter der obigen Grundsatzfrage verbirgt, ist die folgende. Ich habe mir überlegt, dict zu unterklassifizieren, weil ich die db[ ]-Syntax recht häufig verwenden würde (anstatt ständig db.contents[ ] zu verwenden); die einzigen Daten (Attribute) des Objekts sind tatsächlich ein dict. Ich möchte der Datenbank ein paar Methoden hinzufügen (z. B. get_image_by_name() , oder get_image_by_code() zum Beispiel), und überschreiben Sie nur die __init__() denn die Bilddatenbank wird durch das Verzeichnis definiert, in dem sie sich befindet.

Zusammengefasst Die (praktische) Frage könnte lauten: Was ist eine gute Implementierung für etwas, das sich wie ein Wörterbuch verhält, nur dass seine Initialisierung anders ist (es braucht nur einen Verzeichnisnamen) und dass es zusätzliche Methoden hat?

In vielen Antworten wurden "Fabriken" erwähnt. Ich schätze, es läuft alles auf Folgendes hinaus: Unterklassifizieren Sie dict, überschreiben Sie __init__() und fügen Methoden hinzu, oder schreiben Sie eine (Fabrik-)Funktion, die ein Diktat zurückgibt, zu dem Sie Methoden hinzufügen? Ich neige dazu, die erste Lösung zu bevorzugen, weil die Fabrikfunktion ein Objekt zurückgibt, dessen Typ nicht angibt, dass es zusätzliche Semantik und Methoden hat, aber was denken Sie?

Bearbeiten 2 :

Aus den Antworten entnehme ich, dass es keine gute Idee ist, dict zu unterklassifizieren, wenn die neue Klasse "kein Wörterbuch" ist, und insbesondere, wenn ihre __init__ Methode kann nicht die gleichen Argumente wie dicts __init__ (was in der obigen "praktischen Frage" der Fall ist). Mit anderen Worten, wenn ich es richtig verstehe, scheint der Konsens zu sein: Wenn Sie eine Unterklasse bilden, müssen alle Methoden (einschließlich der Initialisierung) die gleiche Signatur wie die Methoden der Basisklasse haben. Dadurch kann isinstance(subclass_instance, dict) garantieren, dass subclass_instance.__init__() kann verwendet werden wie dict.__init__() zum Beispiel.

Eine andere praktische Frage taucht dann auf: wie sollte eine Klasse, die genau wie dict ist, mit Ausnahme der Initialisierungsmethode, implementiert werden? ohne subclassing? dies würde einige lästige Boilerplate-Code erfordern, nicht?

1 Stimmen

Eine Werksfunktion ist der richtige Weg. Wenn Sie die Funktion anpassen müssen Instanz Verhalten, dann sollten Sie eine Unterklasse erstellen. Wenn Sie einfach nur überschreiben wollen Initialisierung brauchen Sie nichts zu untergliedern, da sich Ihre Instanzen nicht von den Standardinstanzen unterscheiden. Denken Sie daran, dass init wird nicht als Teil der Schnittstelle der Instanz, sondern der Klasse betrachtet.

0 Stimmen

Soweit ich es sehe, wäre es bei diesem Problem am besten, die __getitem__ Methode zu Ihrer ImageDB hinzuzufügen, anstatt ein dict zu unterklassifizieren, denn es ist no ein Diktat. Damit können Sie tun, was Sie wollen, sin mit all diesen Methoden wie pop() die für Ihre Klasse ungeeignet zu sein scheinen.

0 Stimmen

@gs: Guter Punkt, über pop; es ist in der Tat, zumindest für den Moment, irrelevant (der Inhalt der Datenbank ist nur bei der Initialisierung definiert). Ich denke, dass es in der Tat am besten ist, wenn die Implementierung eng an die notwendigen Funktionen angepasst ist.

17voto

Alan Franzoni Punkte 2921

Sie sollten wahrscheinlich anrufen dict.__init__(self) beim Unterklassifizieren; tatsächlich wissen Sie nicht, was genau in dict passiert (da es ein builtin ist), und das kann von Version zu Version und von Implementierung zu Implementierung variieren. Ein Nichtaufruf kann zu unangemessenem Verhalten führen, da Sie nicht wissen können, wo dict seine internen Datenstrukturen hält.

Übrigens, Sie haben uns nicht gesagt, was Sie wollen zu tun; wenn Sie eine Klasse mit Diktatverhalten (Mapping) wollen, und Sie brauchen nicht wirklich ein Diktat (z.B. gibt es keinen Code, der isinstance(x, dict) irgendwo in Ihrer Software, so wie es sein sollte), sind Sie wahrscheinlich besser dran, wenn Sie UserDict.UserDict o UserDict.DictMixin wenn Sie mit Python <= 2.5 arbeiten, oder collections.MutableMapping wenn Sie mit Python >= 2.6 arbeiten. Diese werden Ihre Klasse mit einem ausgezeichneten dict-Verhalten ausstatten.

EDIT: Ich habe in einem anderen Kommentar gelesen, dass Sie keine der Methoden von dict überschreiben! Dann gibt es keinen Sinn in subclassing überhaupt, tun Sie es nicht.

def createImageDb(directory):
    d = {}
    # do something to fill in the dict
    return d

EDIT 2: Sie wollen von dict erben, um neue Methoden hinzuzufügen, aber Sie brauchen keine zu überschreiben. Dann könnte eine gute Wahl sein:

class MyContainer(dict):
    def newmethod1(self, args):
        pass

    def newmethod2(self, args2):
        pass

def createImageDb(directory):
    d = MyContainer()
    # fill the container
    return d

Übrigens: Welche Methoden fügen Sie hinzu? Sind Sie sicher, dass Sie eine gute Abstraktion schaffen? Vielleicht ist es besser, eine Klasse zu verwenden, die die von Ihnen benötigten Methoden definiert, und intern ein "normales" Diktat zu verwenden.

Fabrik func: http://en.wikipedia.org/wiki/Factory_method_pattern

Es ist einfach eine Möglichkeit, die Konstruktion einer Instanz an eine Funktion zu delegieren, anstatt ihre Konstruktoren zu überschreiben/zu ändern.

14voto

Anurag Uniyal Punkte 81337

Sie sollten generell die Basisklasse __init__ Warum also hier eine Ausnahme machen?

Entweder nicht außer Kraft setzen __init__ oder wenn Sie Folgendes außer Kraft setzen müssen __init__ Basisklasse aufrufen __init__ Wenn Sie sich um Argumente sorgen, übergeben Sie einfach *args, **kwargs oder nichts, wenn Sie ein leeres Diktat wünschen, z.B.

class MyDict(dict):
    def __init__(self, *args, **kwargs ):
        myparam = kwargs.pop('myparam', '')
        dict.__init__(self, *args, **kwargs )

Wir sollten nicht davon ausgehen, was die Basisklasse tut oder nicht tut, es ist falsch, die Basisklasse nicht aufzurufen __init__

3voto

denis Punkte 20177

Achten Sie auf Pickling, wenn Sie dict unterklassifizieren; dies benötigt zum Beispiel __getnewargs__ in 2.7, und vielleicht __getstate__ __setstate__ in älteren Versionen. (Ich habe keine Ahnung, warum.)

class Dotdict( dict ):
    """ d.key == d["key"] """

    def __init__(self, *args, **kwargs):
        dict.__init__( self, *args, **kwargs )
        self.__dict__ = self

    def __getnewargs__(self):  # for cPickle.dump( d, file, protocol=-1)
        return tuple(self)

2voto

unutbu Punkte 769083

PEP 372 befasst sich mit dem Hinzufügen eines geordneten Diktats zum Modul collections.

Er warnt, dass "die Unterklassifizierung von dict eine nicht triviale Aufgabe ist und viele Implementierungen nicht alle Methoden richtig überschreiben, was zu unerwarteten Ergebnissen führen kann".

Die vorgeschlagene (und akzeptierte) Patch zu Python3.1 verwendet eine __init__ das so aussieht:

+class OrderedDict(dict, MutableMapping):
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        if not hasattr(self, '_keys'):
+            self._keys = []
+        self.update(*args, **kwds)

Daraus ergibt sich folgendes Bild dict.__init__() muss nicht aufgerufen werden.

Bearbeiten: Wenn Sie keines der folgenden Elemente außer Kraft setzen oder erweitern dict Methoden zu verwenden, stimme ich mit Alan Franzoni überein: Verwenden Sie eine Diktatfabrik, anstatt eine Unterklasse zu bilden:

def makeImageDB(*args,**kwargs):
   d = {}
   # modify d
   return d

1voto

prosti Punkte 34344

Wenn Sie vorhaben, eine Unterklasse wie dict Basistyp können Sie auch die UserDict aus Sammlungen. UserDict ist so konzipiert, dass sie unterteilt werden kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X