2010-05-29 14 views
73

Estoy tratando de implementar la funcionalidad de corte para una clase que estoy creando que crea una representación vectorial.Python: Implementando slicing en __getitem__

Tengo este código hasta el momento, que creo que implementará correctamente la división pero cada vez que hago una llamada como v[4] donde v es un vector python devuelve un error acerca de no tener suficientes parámetros. Así que estoy tratando de encontrar la manera de definir el método especial getitem en mi clase para manejar tanto los índices simples como el corte.

def __getitem__(self, start, stop, step): 
    index = start 
    if stop == None: 
     end = start + 1 
    else: 
     end = stop 
    if step == None: 
     stride = 1 
    else: 
     stride = step 
    return self.__data[index:end:stride] 

Respuesta

79

El método __getitem__() recibirá un objeto slice cuando se corta el objeto. Simplemente mire los miembros start, stop y step del objeto slice para obtener los componentes del sector.

>>> class C(object): 
... def __getitem__(self, val): 
...  print val 
... 
>>> c = C() 
>>> c[3] 
3 
>>> c[3:4] 
slice(3, 4, None) 
>>> c[3:4:-2] 
slice(3, 4, -2) 
>>> c[():1j:'a'] 
slice((), 1j, 'a') 
+5

Nota: para extender tipos órdenes internas como lista o tupla que tiene que implementar '__getslice__' para las versiones 2.X pitón. ver https://docs.python.org/2/reference/datamodel.html#object.__getslice__ – gregorySalvan

+0

@gregorySalvan: ¿No es recurrente ese ejemplo de compatibilidad debajo de esa sección? – Eric

+1

@Eric: No, porque la presencia del segundo colon no pasa '__get/set/delslice__'. Aunque es bastante sutil. – user2357112

54

Tengo una lista de "sintético" (uno donde los datos es mayor de lo que se desee crear en la memoria) y mi __getitem__ parece esto:

def __getitem__(self, key) : 
    if isinstance(key, slice) : 
     #Get the start, stop, and step from the slice 
     return [self[ii] for ii in xrange(*key.indices(len(self)))] 
    elif isinstance(key, int) : 
     if key < 0 : #Handle negative indices 
      key += len(self) 
     if key < 0 or key >= len(self) : 
      raise IndexError, "The index (%d) is out of range."%key 
     return self.getData(key) #Get the data from elsewhere 
    else: 
     raise TypeError, "Invalid argument type." 

La rebanada no devuelve el mismo tipo, que es un no- no, pero funciona para mí

+1

No debería si la clave> = len (self) be if key < 0 or key > = len (self)? ¿Qué ocurre si se pasa una clave <-len (auto)? – estan

+0

@estan: Estoy de acuerdo, creo que debería, para cubrir el caso de que usted ha mencionado. –

+0

@estan Tienes razón. Editándolo ahora –

6

¿Cómo se define la clase getitem para manejar los índices simples y el corte?

objetos de división se crea automáticamente cuando se utiliza una coma en la notación de subíndice - y que es lo que se pasa a __getitem__. Utilice isinstance para comprobar si tiene un objeto de división: el uso

from __future__ import print_function 

class Sliceable(object): 

    def __getitem__(self, given): 
     if isinstance(given, slice): 
      # do your handling for a slice object: 
      print(given.start, given.stop, given.step) 
     else: 
      # Do your handling for a plain index 
      print(given) 

Ejemplo:

>>> sliceme = Sliceable() 
>>> sliceme[1] 
1 
>>> sliceme[2] 
2 
>>> sliceme[:] 
None None None 
>>> sliceme[1:] 
1 None None 
>>> sliceme[1:2] 
1 2 None 
>>> sliceme[1:2:3] 
1 2 3 
>>> sliceme[:2:3] 
None 2 3 
>>> sliceme[::3] 
None None 3 
>>> sliceme[::] 
None None None 
>>> sliceme[:] 
None None None 
3

Para extender la respuesta de Aarón, para cosas como numpy, que puede hacer el corte en lonchas multidimensional mediante la comprobación para ver si given es una tuple:

class Sliceable(object): 
    def __getitem__(self, given): 
     if isinstance(given, slice): 
      # do your handling for a slice object: 
      print("slice", given.start, given.stop, given.step) 
     elif isinstance(given, tuple): 
      print("multidim", given) 
     else: 
      # Do your handling for a plain index 
      print("plain", given) 

sliceme = Sliceable() 
sliceme[1] 
sliceme[::] 
sliceme[1:, ::2] 

`` `

Salida:

('plain', 1) 
('slice', None, None, None) 
('multidim', (slice(1, None, None), slice(None, None, 2))) 
+0

Como un menor seguimiento, aquí [es un ejemplo] (https://github.com/EricCousineau-TRI/repro/blob/62151af/bindings/mlmodule/NumPyProxy.m#L83) de emplear esta asignación entre MATLAB indexación y la indexación NumPy (que actualmente no es compatible en MATLAB R2016b), con un [ejemplo de uso] (https://github.com/EricCousineau-TRI/repro/blob/62151af/bindings/mlmodule/test/example_py_slice.m# L43) de eso. –