2012-01-03 16 views
9

¿Hay \ Cómo construiría un equivalente de la muy útil collections.defaultdict de python?defaultdict equivalente para las listas

uso imaginada de un contenedor de este tipo:

>>> a = collections.defaultlist(0) 
>>> a[2]=7 
>>> a[4]='x' 
>>> a 
[0,0,7,0,'x'] 

ACTUALIZACIÓN: He añadido un follow up question para añadir aún más funcionalidad a esta construcción

+0

Utilice el 'Array' de JavaScript ;-) –

+0

@Josh Lee: ¿Leyó las etiquetas? Sin JavaScript, esta es una pregunta de Python. –

+0

¿Por qué querrías eso? –

Respuesta

10

creo que esto sería un poco confuso de usar; Sin embargo, aquí está mi primer pensamiento sobre cómo hacerlo:

class defaultlist(list): 
    def __init__(self, fx): 
     self._fx = fx 

    def __setitem__(self, index, value): 
     while len(self) <= index: 
      self.append(self._fx()) 
     list.__setitem__(self, index, value) 

Esto toma un exigible (creo que eso es cómo funciona defaultdict) para el valor por defecto.

cuando corro:

a = defaultlist(int) 
print a 
a[2] = 7 
a[4] = 'x' 
print a 

regrese:

[] 
[0, 0, 7, 0, 'x'] 
+0

La pregunta parece querer que el valor predeterminado sea un valor, en lugar de que un invocable (por lo que a ese respecto, es diferente de 'defaultdict'). Sería trivial adaptar el suyo para que se ajuste a la pregunta (añada 'self._fx' en lugar de' self._fx() '). –

+0

Ahora intente 'a [4] = 'x''' a [2] = 7' y vea el error en este código. Fácil de arreglar sin embargo. –

+0

@MarkRansom, buena captura y solución. – Finn

3

Si todo lo que necesita es acceso indexado y no cortar/anexar, etc., entonces simplemente use un defaultdict.

(si realmente quiere Perl/js semántica de esto, se podía lista __get__ y __set__ subclase)

2

mi propuesta:

def xtend(f): 
    def wrap(self, index, *args): 
     if len(self) <= index: 
      self.extend([self._gen()] * (index - len(self) + 1)) 
     return f(self, index, *args) 
    return wrap 

class defaultlist(list): 
    def __init__(self, gen, lst = []): 
     list.__init__(self, lst) 
     self._gen = gen 

    __setitem__ = xtend(list.__setitem__) 
    __getitem__ = xtend(list.__getitem__) 

Resultados:

>>> a = defaultlist(int, [1, 2, 3]) 
>>> a[10] = 'x' 
>>> a[2] = 7 
>>> print a 
[1, 2, 7, 0, 0, 0, 0, 0, 0, 0, 'x'] 
0

Tal vez la forma más fácil es usar un diccionario:

>>> a = {} 
>>> a[2] = 7 
>>> a[4] = 'x' 
>>> [a[i] if i in a else 0 for i in xrange(max(a) + 1)] 
[0, 0, 7, 0, 'x'] 
1

Una versión ligeramente mejorada de la respuesta por @Finn.

class defaultlist(list): 
    """List returning default value when accessing uninitialized index. 

    Original implementation: http://stackoverflow.com/a/8719940/315168 
    """ 

    def __init__(self, fx): 
     self._fx = fx 

    def __setitem__(self, index, value): 
     while len(self) <= index: 
      self.append(self._fx()) 
     list.__setitem__(self, index, value) 

    def __getitem__(self, index): 
     """Allows self.dlist[0] style access before value is initialized.""" 
     while len(self) <= index: 
      self.append(self._fx()) 
     return list.__getitem__(self, index) 
Cuestiones relacionadas