Subclase dict: debería dict. init () be called?


He aquí una doble pregunta, con una parte teórica y una práctica:{[15]]}

Cuando subclase dict:

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

¿Debería llamarse dict.__init__(self), solo como una medida de "seguridad" (por ejemplo, en caso de que haya algunos detalles de implementación no triviales que importen)? ¿existe el riesgo de que el código se rompa con una versión futura de Python si dict.__init__() es no llamado? Estoy buscando una razón fundamental para hacer una cosa u otra, aquí (prácticamente, llamar dict.__init__() es seguro).

Mi conjetura es que cuando se llama ImageDB.__init__(self, directory), self ya es un nuevo objeto dict vacío, y que por lo tanto no hay necesidad de llamar dict.__init__ (quiero que el dict esté vacío, al principio). ¿Es correcto?

Edit :

La pregunta más práctica detrás de la pregunta fundamental anterior es la siguiente. Estaba pensando en subclasificar dict porque usaría la sintaxis db [often] bastante a menudo (en lugar de hacer db.contents [all] all the time); los únicos datos del objeto (atributo) es realmente un dictado. Quiero agregar algunos métodos a la base de datos (como get_image_by_name(), o get_image_by_code(), por ejemplo), y solo sobrescribir el __init__(), porque la base de datos de imágenes está definida por el directorio que la contiene.

En resumen , la pregunta (práctica) podría ser: ¿qué es una buena implementación para algo que se comporta como un diccionario, excepto que su inicialización es diferente (solo toma un nombre de directorio), y que tiene adicional métodos?

Las"fábricas" fueron mencionadas en muchas respuestas. Así que supongo que todo se reduce a: ¿subclases dict, anulas __init__() y añades métodos, o escribes una función (de fábrica) que devuelve un dict, a la que añades métodos? Me inclino a preferir la primera solución, porque la función factory devuelve un objeto cuyo tipo no indica que tiene semántica y métodos adicionales, pero ¿qué piensas?

Editar 2:

Deduzco de todo el mundo responda que no es una buena idea subclase dict cuando la nueva clase "no es un diccionario", y en particular cuando su método __init__ no puede tomar los mismos argumentos que dict __init__ (que es el caso en la "pregunta práctica" anterior). En otras palabras, si entiendo correctamente, el consenso parece ser: cuando subclases, todos los métodos (incluida la inicialización) deben tener la misma firma que los métodos de la clase base. Esto permite a isinstance (subclass_instance, dict) garantizar que subclass_instance.__init__() se puede usar como dict.__init__(), por ejemplo.

Luego surge otra pregunta práctica: ¿cómo debería implementarse una clase que es como dict, excepto por su método de inicialización? sin subclases? esto requeriría algún código repetitivo molesto, ¿no?

Author: Eric Lebigot, 2010-01-09

4 answers

Probablemente deberías llamar a dict.__init__(self) cuando subclases; de hecho, no sabes lo que está sucediendo precisamente en dict (ya que es un builtin), y eso puede variar entre versiones e implementaciones. No llamarlo puede resultar en un comportamiento inadecuado, ya que no se puede saber dónde dict está sosteniendo sus estructuras de datos internas.

Por cierto, no nos has dicho lo que quieres que haga; si quieres una clase con comportamiento dict (mapeo), y realmente no necesitas un dict (por ejemplo, no hay código haciendo isinstance(x, dict) en cualquier lugar de su software, como debería ser), probablemente sea mejor usar UserDict.UserDict o UserDict.DictMixin si está en python collections.MutableMapping si está en python >= 2.6 . Estos proporcionarán a su clase un excelente comportamiento dict.

EDIT: He leído en otro comentario que no estás anulando ninguno de los métodos de dict! Entonces no tiene sentido subclasificar en absoluto, no lo hagas.

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

EDIT 2: desea heredar de dict para agregar nuevos métodos, pero no necesita sobrescribir cualquier. Que una buena elección podría ser:

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

    def newmethod2(self, args2):
        pass


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

Por cierto: ¿qué métodos estás agregando? ¿Estás seguro de que estás creando una buena abstracción? Tal vez sea mejor que use una clase que defina los métodos que necesita y use un dict "normal" internamente.

Fábrica func: http://en.wikipedia.org/wiki/Factory_method_pattern

Es simplemente una forma de delegar la construcción de una instancia a una función en lugar de sobrescribir/cambiar sus constructores.

 15
Author: Alan Franzoni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-16 08:25:25

Generalmente deberías llamar a la clase base' __init__ entonces, ¿por qué hacer una excepción aquí?

O bien no sobrescriba __init__ o si necesita sobreescribir __init__ llame a la clase base __init__, Si se preocupa por los argumentos, simplemente pase * args, * * kwargs o nada si desea un diccionario vacío, por ejemplo,

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

No debemos asumir lo que la clase base está haciendo o no, está mal no llamar a la clase base __init__

 10
Author: Anurag Uniyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-04 06:05:01

Tenga cuidado con el decapado cuando subclase dict; esto por ejemplo necesita _ _ getnewargs _ _ en 2.7, y tal vez__ getstate ____ setstate _ _ en versiones anteriores. (No tengo idea de por qué.)

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)
 3
Author: denis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-13 08:31:38

PEP 372 se ocupa de agregar un dict ordenado al módulo colecciones.

Advierte que "la subclase dict es una tarea no trivial y muchas implementaciones no anulan todos los métodos correctamente, lo que puede conducir a resultados inesperados."

El parche propuesto (y aceptado) para python3.1 usa un __init__ que se parece a esto:

+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)

Basado en esto, parece que dict.__init__() no necesita ser llamado.

Editar: Si no está sobreescribiendo o extendiendo cualquiera de los métodos de dict, entonces, estoy de acuerdo con Alan Franzoni: use una fábrica dict en lugar de subclase:

def makeImageDB(*args,**kwargs):
   d = {}
   # modify d
   return d
 2
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-09 14:22:16