2012-01-03 13 views
9

Esiste \ Come si costruisce un equivalente di python molto utile collections.defaultdict?equivalente di default predefinito per gli elenchi

utilizzo Immaginato di un tale contenitore:

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

UPDATE: Ho una follow up question per aggiungere ancora più funzionalità per questo costrutto

+0

Utilizzo di JavaScript 'Array' ;-) –

+0

@Josh Lee: Hai letto i tag a tutti? No JavaScript a tutti-- questa è una domanda Python. –

+0

Perché lo vuoi? –

risposta

10

Penso che questo sarebbe stato un po 'di confusione da usare; Tuttavia, ecco il mio primo pensiero su come farlo:

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) 

Questo richiede un callable (credo che sia come funziona defaultdict) per il valore di default.

quando ho eseguito:

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

torno:

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

La domanda sembra voler che il valore predefinito sia un valore, piuttosto di un callable (quindi a tale riguardo, diverso da 'defaultdict'). Sarebbe banale adattare il tuo per adattarlo alla domanda (aggiungi 'self._fx' piuttosto che' self._fx() '). –

+0

Ora prova 'a [4] = 'x''' a [2] = 7' e vedi il bug in questo codice. Facile da risolvere però. –

+0

@MarkRansom, buona presa e correzione. – Finn

3

Se tutto ciò che serve è accesso indicizzato e non affettare/aggiungere, ecc, quindi basta usare un defaultdict.

(se si vuole veramente Perl/js semantica su questo, si potrebbe creare una sottoclasse lista __get__ e __set__)

2

la mia proposta:

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__) 

Risultati:

>>> 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

Forse il modo più semplice è usare un ditt:

>>> 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 versione leggermente migliorata dalla risposta di @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) 
Problemi correlati