web-dev-qa-db-fra.com

Python: implémentation du découpage dans __getitem__

J'essaie d'implémenter la fonctionnalité de tranche pour une classe que je crée qui crée une représentation vectorielle.

J'ai ce code jusqu'à présent, qui, je crois, implémentera correctement la tranche, mais chaque fois que je fais un appel comme v[4] où v est un vecteur python renvoie une erreur sur le manque de paramètres. J'essaie donc de comprendre comment définir la méthode spéciale getitem dans ma classe pour gérer à la fois des index simples et des tranches.

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]
96
nicotine

La méthode __getitem__() recevra un objet slice lorsque l'objet sera découpé. Regardez simplement les membres start, stop et step de l'objet slice afin d'obtenir les composants de la tranche.

>>> 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')
107

J'ai une liste "synthétique" (une où les données sont plus grandes que celles que vous voudriez créer en mémoire) et mon __getitem__ ressemble à ça:

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 tranche ne renvoie pas le même type, ce qui est non-non, mais cela fonctionne pour moi.

61
Walter Nissen

Comment définir la classe getitem pour gérer à la fois les index simples et le découpage?

Les objets Slice sont automatiquement créés lorsque vous utilisez deux points dans la notation en indice - et ça est ce qui est passé à __getitem__. Utilisez isinstance pour vérifier si vous avez un objet tranche:

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)

Exemple d'utilisation:

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

Python 2, soyez conscient:

Dans Python 2, il existe une méthode obsolète que vous devrez peut-être remplacer lors du sous-classement de certains types intégrés.

De la documentation du modèle de données :

object.__getslice__(self, i, j)

Déconseillé depuis la version 2.0: Prise en charge des objets slice en tant que paramètres de la méthode __getitem__(). (Cependant, les types intégrés de CPython implémentent actuellement __getslice__(). Par conséquent, vous devez le remplacer dans les classes dérivées lors de l'implémentation du découpage.)

Cela a disparu dans Python 3.

13
Aaron Hall

La bonne façon de procéder consiste à avoir __getitem__ prend un paramètre, qui peut être soit un nombre, soit un objet tranche.

Voir:

http://docs.python.org/library/functions.html#slice

http://docs.python.org/reference/datamodel.html#object.__getitem__

7
carl

Pour étendre la réponse d'Aaron, pour des choses comme numpy, vous pouvez faire un découpage multidimensionnel en vérifiant si given est un 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]

`` ''

Sortie:

('plain', 1)
('slice', None, None, None)
('multidim', (slice(1, None, None), slice(None, None, 2)))
6
Eric Cousineau