2011-04-07 26 views
6

Próbuję zbudować klasę, która dziedziczy metody z listy Pythona, ale także robi dodatkowe rzeczy na wierzchu ... prawdopodobnie łatwiej jest pokazać kod w tym momencie ...Emulowanie metody list.insert() jako podklasy listy Pythona

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 

Mój problem polega na tym ... jeśli chcę wykonać tego samego rodzaju testach wstępnych obiekt na insert() jak ja na append(), nie mogę znaleźć drogę do kodeksu nowe metody bez poświęcania wsparcia dla jednej metody rozwijania listy (tj. list.append(), list.insert() lub list.extend()). Jeśli staram się je wszystkie wspierać, kończę pętle rekursywne. Jaki jest najlepszy sposób obejścia tego problemu?

EDIT: I wziął sugestię o instacji collections.MutableSequence zamiast listy Pythona()

Otrzymany kod ... delegowania tutaj w przypadku, pomaga ktoś ...

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 = []' nie robi tego, co myślisz, że robi. –

+1

'super (HostList, self) .__ init __ (self)' zrobiłoby lewę. To, co robi twój kod, zmienia przypisanie zmiennej (self) 'self' do' [] '. –

+0

'__getitem__' zwróci obiekt listy, jeśli określono plasterek. Mógłbyś zmienić inicjator na '__init __ (self, l = None), który użyje listy, jeśli została podana. Następnie w '__getitem__', jeśli ii jest obiektem slice, to zwróć' HostList (self._list [ii]) ' –

Odpowiedz

7

Jeśli możesz tego uniknąć, nie dziedzicz z klas wbudowanych. (Ty może, ale to nie znaczy, że powinny bez naprawdę przekonujące przyczyny)

Klasy te są zoptymalizowane pod kątem szybkości, a to sprawia, że ​​dziedziczenie z nich prawidłowo dość uciążliwe, ponieważ kończy się konieczności zastąpić prawie wszystko.

Zamiast tego, dziedzicząc z collections.MutableSequence, można zaimplementować tylko kilka podstawowych metod i uzyskać solidną, w pełni funkcjonalną implementację API sekwencji, bez wszystkich dziwactw i zastrzeżeń, które pochodzą z dziedziczenia z list.

+0

Świetny punkt +1 ... Po pewnych zmaganiach pracowałem z 'collections.MutableSequence' ... Dziękuję! –

6

Użyj obiektu isinstance, aby sprawdzić obiekty, aby zobaczyć, czy są to instancje Host i użyj super (np. super(HostList, self).insert(...)), aby korzystać z funkcji list, zamiast ponownie wprowadzać ją samodzielnie.

Należy skończyć z czymś takim:

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

Jeszcze lepiej, zdefiniuj statyczną metodę 'as_host (x)', która zwraca 'x', jeśli jest hostem, w przeciwnym razie zgłasza odpowiedni wyjątek. Wtedy połączenia typu "super" stają się jednolinijkowe. –

0

Można zadzwonić list „s metoda od wybranej metody, z super(). W ten sposób nie musisz kłaść go innymi metodami.

3

Jeśli nie ma przekonującego powodu, aby pojemnik HostList całkowicie obsługiwał zmienny interfejs kontenera, sugeruję użycie modelu has-a zamiast is-a. Dodatkowym obciążeniem będzie zapewnienie zgodności typów z operacjami, takimi jak cięcie (zwracanie kontenera HostList zamiast listy).