2010-11-04 17 views
31

Estoy interesado en usar el objeto de la lista de python, pero con una funcionalidad ligeramente alterada. En particular, me gustaría que la lista tenga 1 índice en lugar de 0 indexados. Ej .:¿Cómo heredar y extender un objeto de lista en Python?

>> mylist = MyList() 
>> mylist.extend([1,2,3,4,5]) 
>> print mylist[1] 

salida debe ser: 1

Pero cuando cambié los métodos __getitem__() y __setitem__() hacer esto, yo estaba recibiendo un error RuntimeError: maximum recursion depth exceeded. Yo vanamente alrededor con estos métodos mucho, pero esto es básicamente lo que tenía allí:

class MyList(list): 
    def __getitem__(self, key): 
     return self[key-1] 
    def __setitem__(self, key, item): 
     self[key-1] = item 

Supongo que el problema es que es en sí mismo self[key-1] a llamar al mismo método que está definiendo. Si es así, ¿cómo hago que use el método list() en lugar del método MyList()? Intenté usar super[key-1] en lugar de self[key-1] pero eso resultó en la queja TypeError: 'type' object is unsubscriptable

¿Alguna idea? ¡También si pudieras señalarme un buen tutorial para esto sería genial!

Gracias!

+8

Esto viola el Principio de Sustitución de Liskov bastante descaradamente. Puede que no haya mucho valor en la subclasificación de 'list' si no se puede usar en cualquier lugar donde se espere una' list'. Quizás la composición sería una estrategia más apropiada en este caso? – Ken

+0

No entiendo del todo; ¿Qué quieres decir con "composición"? Además, ¿cómo podemos estar seguros de que no podemos sustituir MyList en lugar de una lista estándar? ¿Hay una buena manera de saberlo? Por ejemplo, si los métodos internos usan '__getitem__' y' __setitem__', ¿habría algún problema? – mindthief

+2

mindthief: http://en.wikipedia.org/wiki/Composition_over_inheritance - y si cambia la interfaz existente de 'list' (es decir, exactamente lo que está pidiendo aquí), entonces no puede sustituir un' MyList' para una 'lista'. Incluso cosas básicas como 'x [0]' no funcionarán. – Ken

Respuesta

41

utilizar la función super() para llamar al método de la clase base, o invocar el método directo:

o

class MyList(list): 
    def __getitem__(self, key): 
     return super(MyList, self).__getitem__(key-1) 

Sin embargo, esto no va a cambiar el comportamiento de otros métodos de listas . Por ejemplo, el índice se mantiene sin cambios, lo que puede conducir a resultados inesperados:

numbers = MyList() 
numbers.append("one") 
numbers.append("two") 

print numbers.index('one') 
>>> 1 

print numbers[numbers.index('one')] 
>>> 'two' 
+2

Además, tenga cuidado con otros métodos de lista que se rompen porque está cambiando un comportamiento de indexación que es una parte fundamental de los objetos de lista. –

+1

Haré +1 si nota por qué ocurre la recursión. Le das la solución sin explicar el problema. –

+8

Por qué se produce la recursión se explicó perfectamente en la pregunta. –

21

En cambio, número entero subclase utilizando el mismo método para definir todos los números para ser uno menos de lo que las plantes a. Voila.

Lo siento, tuve que hacerlo. Es como la broma sobre Microsoft que define a la oscuridad como el estándar.

11

Puede evitar violar el principio de sustitución Liskov creando una clase que hereda de collections.MutableSequence, que es una clase abstracta. Se vería algo como esto:

class MyList(collections.MutableSequence): 
def __init__(self, l=[]): 
    if type(l) is not list: 
     raise ValueError() 

    self._inner_list = l 

def __len__(self): 
    return len(self._inner_list) 

def __delitem__(self, index): 
    self._inner_list.__delitem__(index - 1) 

def insert(self, index, value): 
    self._inner_list.insert(index - 1, value) 

def __setitem__(self, index, value): 
    self._inner_list.__setitem__(index - 1, value) 

def __getitem__(self, index): 
    return self._inner_list.__getitem__(index - 1) 

Hay un problema aquí (aunque puede haber más). Si el índice de su nueva lista como esta:

l = MyList() 
l[0] 

realidad se va a llamar:

self._inner_list[-1] 

que conseguir que el último elemento. Por lo tanto, debe hacer comprobaciones adicionales en los métodos y asegurarse de mantener la indexación inversa, si desea tener esa característica para su lista.

EDIT:

Aquí está el código nuevo, que creo que no debería tener ningún problema.

def indexing_decorator(func): 

    def decorated(self, index, *args): 
     if index == 0: 
      raise IndexError('Indices start from 1') 
     elif index > 0: 
      index -= 1 

     return func(self, index, *args) 

    return decorated 


class MyList(collections.MutableSequence): 
    def __init__(self): 
     self._inner_list = list() 

    def __len__(self): 
     return len(self._inner_list) 

    @indexing_decorator 
    def __delitem__(self, index): 
     self._inner_list.__delitem__(index) 

    @indexing_decorator 
    def insert(self, index, value): 
     self._inner_list.insert(index, value) 

    @indexing_decorator 
    def __setitem__(self, index, value): 
     self._inner_list.__setitem__(index, value) 

    @indexing_decorator 
    def __getitem__(self, index): 
     return self._inner_list.__getitem__(index) 

    def append(self, value): 
     self.insert(len(self) + 1, value) 
+0

'x = MyList(); x.append (4); y = MyList(); imprimir (y) 'Pruébelo. Obtienes resultados interesantes ... – zondo

+1

Casi allí. No debe ajustar el índice para 'insertar'. Además, trate de manejar el caso donde índice == 0. –

+0

Sí, tiene razón. Pero entonces la indexación no sería consistente. Edité el código en la respuesta un poco. Debería funcionar bien ahora, creo. –

Cuestiones relacionadas