2010-03-06 20 views
56

Sto eseguendo il debug di alcuni codici e voglio scoprire quando si accede a un determinato dizionario. Bene, in realtà è una classe che sottoclasse dict e implementa un paio di funzioni extra. Ad ogni modo, quello che mi piacerebbe fare è sottoclasse dict e aggiungere l'override __getitem__ e __setitem__ per produrre un output di debug. In questo momento, hoCome sottoclasse correttamente dict e override __getitem__ & __setitem__

class DictWatch(dict): 
    def __init__(self, *args): 
     dict.__init__(self, args) 

    def __getitem__(self, key): 
     val = dict.__getitem__(self, key) 
     log.info("GET %s['%s'] = %s" % str(dict.get(self, 'name_label')), str(key), str(val))) 
     return val 

    def __setitem__(self, key, val): 
     log.info("SET %s['%s'] = %s" % str(dict.get(self, 'name_label')), str(key), str(val))) 
     dict.__setitem__(self, key, val) 

'name_label' è una chiave che finirà per essere impostato che voglio utilizzare per identificare l'uscita. Ho quindi cambiato la classe che sto strumentando alla sottoclasse DictWatch anziché dict e ho cambiato la chiamata al supercostruttore. Tuttavia, nulla sembra accadere. Pensavo di essere intelligente, ma mi chiedo se dovrei andare in una direzione diversa.

Grazie per l'aiuto!

+0

Hai provato a utilizzare la stampa anziché il registro? Inoltre, potresti spiegare come si crea/si configura il registro? – pajton

+0

Non 'dict .__ init__' prendi' * args'? –

+1

Sembra un buon candidato per un decoratore. –

risposta

24

Quello che stai facendo dovrebbe funzionare assolutamente. Ho testato la tua classe e, a parte una parentesi aperta mancante nelle tue dichiarazioni di registro, funziona perfettamente. Ci sono solo due cose che posso pensare. Innanzitutto, l'output dell'istruzione del tuo registro è impostato correttamente? Potrebbe essere necessario inserire uno logging.basicConfig(level=logging.DEBUG) nella parte superiore dello script.

Secondo, __getitem__ e __setitem__ vengono chiamati solo durante gli accessi []. Quindi assicuratevi di accedere solo DictWatch via d[key], piuttosto che d.get() e d.set()

+0

In realtà non è un paren extra, ma un paren di apertura mancante attorno a '(str (dict.get (self, 'name_label')), str (chiave), str (val)))' – cobbal

+3

True. Per l'OP: per riferimento futuro, puoi semplicemente fare il log.informazioni ('% s% s% s', a, b, c), invece di un operatore di formattazione di stringhe Python. – BrainCore

+0

Ho appena trovato la formattazione delle stringhe più naturale poiché lo faccio in molti casi. Per quanto riguarda la parentesi, ho scritto male qui, perché Python non si lamenta di eventuali errori. –

7

che non dovrebbe davvero cambiare il risultato (che dovrebbe funzionare, per i buoni valori di soglia di registrazione): tuo init dovrebbe essere:

def __init__(self,*args,**kwargs) : dict.__init__(self,*args,**kwargs) 

invece, perché se chiami il tuo metodo con DictWatch ([(1,2), (2,3)]) o DictWatch (a = 1, b = 2) questo fallirà.

(o, meglio, non definire un costruttore per questo)

+0

Sono solo preoccupato per la forma di accesso di 'dict [chiave]', quindi questo non è un problema. –

54

Un altro problema quando sottoclasse dict è che il built-in __init__ non chiama update, e il built-in update non chiama __setitem__. Quindi, se si desidera che tutte le operazioni SetItem di passare attraverso la vostra funzione __setitem__, è necessario assicurarsi che venga chiamato da soli:

class DictWatch(dict): 
    def __init__(self, *args, **kwargs): 
     self.update(*args, **kwargs) 

    def __getitem__(self, key): 
     val = dict.__getitem__(self, key) 
     print 'GET', key 
     return val 

    def __setitem__(self, key, val): 
     print 'SET', key, val 
     dict.__setitem__(self, key, val) 

    def __repr__(self): 
     dictrepr = dict.__repr__(self) 
     return '%s(%s)' % (type(self).__name__, dictrepr) 

    def update(self, *args, **kwargs): 
     print 'update', args, kwargs 
     for k, v in dict(*args, **kwargs).iteritems(): 
      self[k] = v 
+0

Se stai usando Python 3, ti consigliamo di cambiare questo esempio in modo che 'print' sia la funzione' print() 'e il metodo' update() 'usa' items() 'invece di' iteritems() ' . –

0

Tutto quello che dovrete fare è

class BatchCollection(dict): 
    def __init__(self, inpt={}): 
     super(BatchCollection, self).__init__(inpt) 

Un esempio dell'uso di mio uso personale

### EXAMPLE 
class BatchCollection(dict): 
    def __init__(self, inpt={}): 
     super(BatchCollection, self).__init__(inpt) 

    def __setitem__(self, key, item): 
     if (isinstance(key, tuple) and len(key) == 2 
       and isinstance(item, collections.Iterable)): 
      # self.__dict__[key] = item 
      super(BatchCollection, self).__setitem__(key, item) 
     else: 
      raise Exception(
       "Valid key should be a tuple (database_name, table_name) " 
       "and value should be iterable") 

Nota: testato solo in python3

Problemi correlati