2012-02-20 9 views
5

Sto cercando di mantenere un dizionario di file aperti per dividere i dati in singoli file. Quando richiedo un file dal dizionario mi piacerebbe che fosse aperto se la chiave non è presente. Tuttavia, non sembra che io possa usare un lambda come predefinito.è possibile utilizzare un lambda come dizionario predefinito?

ad es.

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

Questo non funziona perché f è impostato sulla funzione, piuttosto che sul risultato. setdefault utilizzando la sintassi sopra non funziona neanche. C'è qualcosa che posso fare oltre a questo:

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

risposta

7

Questo caso d'uso è troppo complesso per un defaultdict, motivo per cui non credo che esista qualcosa di simile nel stdlib Python. Si può tuttavia facilmente scrivere un generico "esteso" defaultdict te stesso, che passa la chiave mancante per il callback:

from collections import defaultdict 

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

Usage:

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

Questo funziona in Python 2.7+, non conoscono le versioni più vecchie :) Inoltre, non dimenticare di chiudere di nuovo i file:

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

Si potrebbe avvolgere il get-e-aperta in un oggetto di classe __getitem__() abbastanza facilmente - qualcosa 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

Un'altra opzione per una sottoclasse che dovrebbe fare quello che ti serve:

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, forse più in generale - per consentire di default che sono valori o espressioni callable:

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

Puoi usare defaultdict dal modulo collections

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

Anche se potrebbe essere migliore di fare solo

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

Questo è il motivo esatto per cui dict[key] sintassi solleva KeyError:

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

dovrebbe diventare:

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

get() non solleva un errore chiave se l'oggetto non viene trovato. [] la notazione lo fa. per esempio. file [tasto] – Jacob

Problemi correlati