2011-04-07 12 views
6

Sto cercando di costruire una classe che eredita i metodi dalla lista di Python, ma fa anche alcune cose aggiuntive in cima ... è probabilmente più facile solo per mostrare il codice a questo punto ...Emulando il metodo list.insert() come una sottoclasse di lista di Python

class Host(object): 
    """Emulate a virtual host attached to a physical interface""" 
    def __init__(self): 
    # Insert class properties here... 
    pass 

class HostList(list): 
    """A container for managing lists of hosts""" 
    def __init__(self): 
     self = [] 

    def append(self, hostobj): 
     """append to the list...""" 
     if hostobj.__class__.__name__ == 'Host': 
      self.insert(len(self), hostobj) 
     else: 
      _classname = hostobj.__class__.__name__ 
      raise RuntimeError, "Cannot append a '%s' object to a HostList" % _classname 

mio problema è questo ... se voglio eseguire lo stesso tipo di prove di ammissione degli oggetti su insert() come ho fatto su append(), non riesco a trovare un modo per codificare il nuovi metodi senza sacrificare il supporto per un metodo di espansione di un elenco (ad esempio list.append(), list.insert() o list.extend()). Se provo a supportarli tutti, finisco con loop ricorsivi. Qual è il modo migliore per risolvere questo problema?

EDIT: ho preso il suggerimento su sottoclassi collections.MutableSequence al posto della lista di Python()

Il codice risultante ... distacco qui nel caso in cui, aiuta qualcuno ...

from collections import MutableSequence 
class HostList(MutableSequence): 
    """A container for manipulating lists of hosts""" 
    def __init__(self, data): 
     super(HostList, self).__init__() 
     if (data is not None): 
      self._list = list(data) 
     else: 
      self._list = list() 

    def __repr__(self): 
     return "<{0} {1}>".format(self.__class__.__name__, self._list) 

    def __len__(self): 
     """List length""" 
     return len(self._list) 

    def __getitem__(self, ii): 
     """Get a list item""" 
     return self._list[ii] 

    def __delitem__(self, ii): 
     """Delete an item""" 
     del self._list[ii] 

    def __setitem__(self, ii, val): 
     # optional: self._acl_check(val) 
     return self._list[ii] 
    def __str__(self): 
     return str(self._list) 
    def insert(self, ii, val): 
     # optional: self._acl_check(val) 
     self._list.insert(ii, val) 
    def append(self, val): 
     self.insert(len(self._list), val) 
+4

'self = []' non fa quello che pensi che faccia. –

+1

'super (HostList, self) .__ init __ (self)' farebbe il trucco. Quello che fa il tuo codice è riassegnare la variabile (argomento) 'self' a' [] '. –

+0

'__getitem__' restituirà un oggetto lista se viene specificata una porzione. È possibile modificare l'inizializzatore in '__init __ (self, l = None)' che utilizzerà un elenco se fornito. Quindi in '__getitem__', se ii è un oggetto fetta, quindi restituisci' HostList (self._list [ii]) ' –

risposta

7

Se è possibile evitarlo, non ereditarlo dalle classi incorporate. (È possibile , ma questo non si vuol dire dovrebbe senza una ragione davvero convincente)

Le classi sono ottimizzati per la velocità, e che fa ereditare da loro correttamente abbastanza noioso, dal momento che si finisce per dover sostituire quasi tutto.

Eredita da collections.MutableSequence invece consente di implementare solo alcuni metodi essenziali e ottenere un'implementazione completa e completa dell'API di sequenza, senza tutte le stranezze e gli avvertimenti che derivano dall'integrazione di list.

+0

Grande Punto 1 ... Dopo alcune lotte, ho ottenuto lavorando con 'collections.MutableSequence' ... Grazie! –

6

Utilizzare isinstance per verificare se gli oggetti sono istanze di Host e utilizzare super (ad esempio super(HostList, self).insert(...)) per utilizzare la funzionalità di list, anziché reimplementare da soli.

Si dovrebbe finire con qualcosa di simile:

def append(self, obj): 
    """append to the list...""" 
    if not isinstance(obj, Host): 
     raise RuntimeError, "Cannot append a '%s' object to a HostList" % obj.__class__.__name__ 
    super(HostList, self).append(obj) 
+1

Ancora meglio, definire un metodo statico 'as_host (x)' che restituisce 'x' se si tratta di un host, altrimenti genera l'eccezione appropriata. Quindi le chiamate "super" diventano one-liner. –

0

Si può chiamare list 's metodo dal metodo, con super(). In questo modo non è necessario rimuoverlo con gli altri metodi.

3

A meno che se c'è un motivo valido per avere il vostro contenitore ElencoHost supporta completamente l'interfaccia del contenitore mutevole, io suggerisco di usare un ha-un modello piuttosto che è-un. Avrai l'onere aggiuntivo di garantire la coerenza del tipo con operazioni come slicing (restituisci un contenitore HostList piuttosto che un elenco).