2012-02-20 15 views
5

Estoy tratando de mantener un diccionario de archivos abiertos para dividir datos en archivos individuales. Cuando solicito un archivo del diccionario, me gustaría que se abriera si la clave no está allí. Sin embargo, no parece que pueda usar un lambda por defecto.¿es posible usar un lambda como valor predeterminado del diccionario?

p. Ej.

files = {} 
for row in data: 
    f = files.get(row.field1, lambda: open(row.field1, 'w')) 
    f.write('stuff...') 

Esto no funciona porque f está configurado para la función, en lugar de su resultado. setdefault utilizando la sintaxis anterior tampoco funciona. ¿Hay algo que pueda hacer además de esto:

f = files.get(row.field1) 
if not f: 
    f = files[row.field1] = open(row.field1, 'w') 

Respuesta

7

Este caso de uso es demasiado complejo para un defaultdict, por lo que no creo que algo así existe en el stdlib Python. Sin embargo, puede escribir fácilmente un genérico "extendida" defaultdict a sí mismo, que pasa la clave que falta para la devolución de llamada:

from collections import defaultdict 

class BetterDefaultDict(defaultdict): 
    def __missing__(self, key): 
    return self.setdefault(key, self.default_factory(key)) 

Uso:

>>> files = BetterDefaultDict(lambda key: open(key, 'w')) 
>>> files['/tmp/test.py'] 
<open file '/tmp/test.py', mode 'w' at 0x7ff552ad6db0> 

Esto funciona en Python 2.7+, no conocen versiones anteriores :) Además, no se olvide de cerrar los archivos de nuevo:

finally: 
    for f in files.values(): f.close() 
2

Usted puede envolver el conseguir-y-abierta en __getitem__() muy fácilmente de un objeto de clase - algo li ke:

class FileCache(object): 
    def __init__(self): 
     self.map = {} 

    def __getitem__(self,key): 
     if key not in self.map:    
      self.map[key] = open(key,'w') 
     return self.map.key 
1

Otra opción para una subclase que debe hacer lo que necesita:

class LambdaDefaultDict(dict): 

    def get(self, key, default=None): 
     try: 
      return self[key] 
     except KeyError: 
      return default() 

    def setdefault(self, key, default=None): 
     if not self.has_key(key): 
      self[key] = default() if default else None 
     return self[key] 

O, tal vez más general - para permitir que los valores predeterminados que son valores o expresiones que se puede llamar:

class CallableDefaultDict(dict): 

    def get(self, key, default=None): 
     try: 
      return self[key] 
     except KeyError: 
      return default() if callable(default) else default 

    def setdefault(self, key, default=None): 
     if not self.has_key(key): 
      self[key] = default() if callable(default) else default 
     return self[key] 
1

Puede usar defaultdict del módulo de colecciones

class FileCache(collections.defaultdict): 
    def __missing__(self, key): 
    fo = open(key, 'w') 
    self[key] = fo 
    return fo 

Aunque podría ser mejor simplemente hacer

files = {} 
def get_file(f): 
    fo = files.get(f) 
    if fo is None: 
    fo = open(f, 'w') 
    files[f] = fo 
    return fo 

for row in data: 
    f = get_file(row.field1) 
    f.write('stuff...') 
1

Ésta es la razón exacta por la dict[key] sintaxis plantea KeyError:

files = {} 
for row in data: 
    f = files.get(row.field1, lambda: open(row.field1, 'w')) 
    f.write('stuff...') 

debe convertirse en:

files = {} 
for row in data: 
    try: f = files[row.field1] 
    except KeyError: f = open(row.field1, 'w') 
    f.write('stuff...') 
+1

get() no plantea un error clave si el artículo no se encuentra. [] la notación sí lo hace. p.ej. archivos [clave] – Jacob

Cuestiones relacionadas