2010-01-09 15 views
29

Aquí está una doble pregunta, con una parte teórica y una práctica:Subclassing dict: se debe llamar a dict .__ init __()?

Cuando la subclasificación dict:

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

debe dict.__init__(self) ser llamado, simplemente como una medida de "seguridad" (por ejemplo, en el caso hay algunos detalles de implementación no triviales que importan) ¿Existe un riesgo de que el código se rompa con una versión futura de Python si se llama dict.__init__()no? Estoy buscando una razón fundamental para hacer una cosa o la otra, aquí (prácticamente, llamar al dict.__init__() es seguro).

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

Editar:

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 con bastante frecuencia (en lugar de hacer db.contents [...] todo el tiempo); el único dato (atributo) del objeto es realmente un dict. Deseo agregar algunos métodos a la base de datos (como get_image_by_name(), o get_image_by_code(), por ejemplo), y solo anular __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 el nombre de un directorio) y tiene métodos?

"Fábricas" se mencionaron en muchas respuestas. Así que supongo que todo se reduce a: ¿subclasifica dict, anula __init__() y agrega métodos, o escribe una función (de fábrica) que devuelve un dict, al que agrega métodos? Me inclino a preferir la primera solución, porque la función de fábrica devuelve un objeto cuyo tipo no indica que tenga semántica y métodos adicionales, pero ¿qué piensas?

Editar 2:

que deduzco de la respuesta de todo el mundo que no es una buena idea a la subclase dict cuando la nueva clase "no es un diccionario", y en particular cuando su método __init__ no puede tener el mismo argumentos como dict's __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 que isinstance (subclass_instance, dict) garantice que subclass_instance.__init__() se pueda usar como dict.__init__(), por ejemplo.

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

+0

Una función de fábrica es el camino a seguir. Si necesita personalizar el comportamiento * instancia *, entonces puede querer crear una subclase. Si solo desea anular la * inicialización *, no necesita crear subclases, ya que sus instancias no son diferentes de las estándar. Recuerde que __init__ no se considera parte de la interfaz de la instancia, sino de la clase. –

+0

Por lo que veo para este problema, sería mejor agregar el método '__getitem__' a su ImageDB en lugar de subclasificar un dict, porque no es un dict. Esto le permite hacer lo que quiera, _sin_ tener todos los métodos como 'pop()' que parecen ser inapropiados para su clase. –

+0

@gs: Buen punto, sobre pop; es, de hecho, al menos por el momento, irrelevante (el contenido de la base de datos se define solo en la inicialización). Creo que de hecho es mejor que la implementación se ajuste perfectamente a las características necesarias. – EOL

Respuesta

15

Probablemente deberías llamar al dict.__init__(self) al crear una subclasificación; de hecho, no se sabe lo que está sucediendo precisamente en dict (ya que es un built-in), y eso puede variar entre versiones e implementaciones. No llamarlo puede provocar un comportamiento incorrecto, ya que no puede saber dónde se encuentra el dict en sus estructuras internas de datos.

Por cierto, no nos dijo lo que quiere hacer; 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 parte de tu software, como debería ser), probablemente estés mejor usando UserDict.UserDict o UserDict.DictMixin si estás en python < = 2.5, o collections.MutableMapping si estás en python> = 2.6. Esos proporcionarán a su clase un excelente comportamiento dict.

EDIT: ¡Leí en otro comentario que no está anulando ningún método de dict! Entonces no tiene sentido hacer subclases en absoluto, no lo hagas.

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

EDIT 2: desea heredar de dict añadir nuevos métodos, pero no es necesario para anular cualquier. Que una buena opció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 uses una clase que defina los métodos que necesitas y usa un dictado "normal" internamente.

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

Es simplemente una manera de delegar la construcción de una instancia a una función en lugar de anular/cambio de sus constructores.

+2

+1: subclases cuando no hay necesidad de subclase es una mala idea, una fábrica es mucho mejor. –

+0

Incluso si no anulo los métodos dict, la nueva clase tiene métodos adicionales, ... (Estoy investigando fábricas, gracias por el puntero!) – EOL

+0

No estoy seguro acerca de UserDict: la documentación dice "Este módulo también define una clase, UserDict, que actúa como un envoltorio alrededor de los objetos del diccionario. La necesidad de esta clase ha sido suplantada en gran parte por la capacidad de subclase directamente desde dict (una característica que comenzó a estar disponible comenzando con Python versión 2.2). – EOL

2

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

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

La propuesta (y aceptado) patch a python3.1 utiliza un __init__ que tiene este aspecto:

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

base a esto, se ve como dict.__init__() no tiene que ser llamado

. Edit: Si no está anulando o extendiendo ninguno de los métodos de dict, entonces, estoy de acuerdo con Alan Franzoni: use una fábrica de dict en lugar de crear subclases:

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

Esto es interesante. Ahora, no llamar a 'dict .__ init __()' con Python 3.1 es seguro, pero ¿qué pasa con el futuro? Como no anulo ningún método, en ImageDB, la creación de subclases es muy segura; solo la inicialización es especial (construye el dict). – EOL

+0

Lo sentimos EOL, no te estoy siguiendo. En mi opinión, Python 3.1 es el futuro ... :) – unutbu

+0

Tenga en cuenta lo que el init está haciendo realmente. Actualiza el dict con todos los argumentos y palabras clave. Eso es algo que tu clase tendrá que hacer, así que llamar a dict. \ _ \ _ Init __ (self, * args, ** kwds) probablemente se encargue de eso, o tendrás que llamar a self.update, como el OrderedDict. hace. –

10

En general, debe llamar a la clase base '__init__, entonces ¿por qué hacer una excepción aquí?

o bien no anula __init__ o si necesita reemplazar __init__ llamada clase base __init__, Si usted se preocupa sólo tiene que pasar argumentos args *, ** kwargs o nada si desea vacío dict por ejemplo,

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

No debemos asumir lo baseclass está haciendo o no, no es correcto, no para llamar a la clase base __init__

+0

Llamar al dict '__init__' es de hecho lo Actualmente estoy haciendo. Ya que parece que llamarlo sin argumentos no hace nada, solo tengo curiosidad acerca de los hechos fundamentales sobre Python que permitirían que no se llame. – EOL

+0

@EOL, IMO es simplemente incorrecto no llamar a la clase base __init__, hasta que haya una razón muy poderosa para hacerlo –

+0

@Anurag: veo tu punto. Estoy tratando de impulsar mi conocimiento de Python un poco más allá, y me preguntaba si existe una "razón muy muy fuerte" para no llamar a 'dict .__ init __ (self)' (sin ningún otro argumento) (como "lo hará nunca hagas nada "). – EOL

3

Cuidado de decapado cuando la subclasificación dict; esto, por ejemplo, necesita __getnewargs__ en 2.7, y tal vez __getstate__ __setstate__ en versiones anteriores. (No tengo ni idea de porqué.)

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) 
Cuestiones relacionadas