2010-05-26 14 views
57

una clase tiene un constructor que toma un parámetro:¿Existe alguna manera inteligente de pasar la clave a default_factory?

class C(object): 
    def __init__(self, v): 
     self.v = v 
     ... 

En algún lugar del código, que es útil para los valores de un diccionario para conocer sus claves.
Quiero usar un defaultdict con la llave pasada a los valores por defecto del recién nacido:

d = defaultdict(lambda : C(here_i_wish_the_key_to_be)) 

¿Alguna sugerencia?

+0

por lo qué problema tienes? – SilentGhost

+3

'd = defaultdict (clave lambda: C (clave))'? –

+0

@jellybean: ¿cómo harías que funcione? – SilentGhost

Respuesta

87

Difícilmente califica como inteligente - pero subclases es su amigo:

class keydefaultdict(defaultdict): 
    def __missing__(self, key): 
     if self.default_factory is None: 
      raise KeyError(key) 
     else: 
      ret = self[key] = self.default_factory(key) 
      return ret 

d = keydefaultdict(C) 
d[x] # returns C(x) 
+8

Esa es exactamente la fealdad que estoy tratando de evitar ... Incluso el uso de un diccionario simple y la comprobación de la existencia clave es mucho más limpio. –

+0

@Paul: y sin embargo, esta es su respuesta. ¿Fealdad? ¡Venga! – tzot

+4

Creo que voy a tomar ese código y ponerlo en mi módulo personalizado de utilidades generales para que pueda usarlo cuando quiera. No demasiado feo de esa manera ... – weronika

7

No creo que necesita defaultdict aquí en absoluto. ¿Por qué no usar el método dict.setdefault?

>>> d = {} 
>>> d.setdefault('p', C('p')).v 
'p' 

Eso, por supuesto, crearía muchos casos de C. En caso de que sea un problema, creo que el enfoque más simple hará:

>>> d = {} 
>>> if 'e' not in d: d['e'] = C('e') 

sería más rápido que el defaultdict o cualquier otra alternativa por lo que yo puedo ver.

ETA con respecto a la velocidad de in prueba contra el uso de try-except:

>>> def g(): 
    d = {} 
    if 'a' in d: 
     return d['a'] 


>>> timeit.timeit(g) 
0.19638929363557622 
>>> def f(): 
    d = {} 
    try: 
     return d['a'] 
    except KeyError: 
     return 


>>> timeit.timeit(f) 
0.6167065411074759 
>>> def k(): 
    d = {'a': 2} 
    if 'a' in d: 
     return d['a'] 


>>> timeit.timeit(k) 
0.30074866358404506 
>>> def p(): 
    d = {'a': 2} 
    try: 
     return d['a'] 
    except KeyError: 
     return 


>>> timeit.timeit(p) 
0.28588609450770264 
+2

Esto es altamente derrochador en los casos en que se accede a d muchas veces y rara vez falta una tecla: C (clave) creará toneladas de objetos innecesarios para que el GC los recopile. Además, en mi caso hay un dolor adicional, ya que la creación de nuevos objetos C es lenta. –

+0

@Paul: eso es correcto. Sugeriría un método aún más simple, ver mi edición. – SilentGhost

+0

No estoy seguro de que sea más rápido que el default, pero esto es lo que suelo hacer (ver mi comentario a la respuesta de THC4k). Esperé que haya una manera simple de hackear el hecho de que default_factory no toma argumentos, para mantener el código un poco más elegante. –

12

No, no lo hay.

La implementación defaultdict no se puede configurar para pasar el key que falta al default_factory out-of-the-box. Su única opción es poner en práctica su propio defaultdict subclase, según lo sugerido por @JochenRitzel, arriba.

Pero eso no es "inteligente" o casi tan limpio como una solución biblioteca estándar sería (si existiera). Por lo tanto, la respuesta a su pregunta sucinta, sí/no es claramente "No".

Es una lástima que la biblioteca estándar le falta una herramienta de este tipo con frecuencia es necesario.

Cuestiones relacionadas