Comprendre la notation des tranches

J'ai besoin d'une bonne explication (les références sont un plus) sur la notation des tranches de Python.

Pour moi, cette notation a besoin d'être améliorée.

Elle semble extrêmement puissante, mais je n&#8217ai pas encore réussi à la comprendre.

Solution

C'est assez simple, vraiment :

a[start:stop]  # items start through stop-1
a[start:]      # items start through the rest of the array
a[:stop]       # items from the beginning through stop-1
a[:]           # a copy of the whole array

Il y a aussi la valeur step, qui peut être utilisée avec n'importe lequel des éléments ci-dessus :

a[start:stop:step] # start through not past stop, by step

Le point clé à retenir est que la valeur :stop représente la première valeur qui n'est pas dans la tranche sélectionnée. Ainsi, la différence entre stop et start est le nombre d'éléments sélectionnés (si step est 1, la valeur par défaut).

L'autre caractéristique est que start ou stop peut être un nombre négatif, ce qui signifie qu'il compte à partir de la fin du tableau au lieu du début. Ainsi :

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

De même, step peut être un nombre négatif :

a[::-1]    # all items in the array, reversed
a[1::-1]   # the first two items, reversed
a[:-3:-1]  # the last two items, reversed
a[-3::-1]  # everything except the last two items, reversed

Python est gentil avec le programmeur s'il y a moins d'éléments que ce que vous demandez. Par exemple, si vous demandez a[:-2] et que a ne contient qu'un seul élément, vous obtenez une liste vide au lieu d'une erreur. Parfois, vous préférez l'erreur, donc vous devez être conscient que cela peut arriver.

Relation avec l'objet slice().

L'opérateur de découpage en tranches [] est en fait utilisé dans le code ci-dessus avec un objet slice() en utilisant la notation : (qui n'est valide que dans []), c'est-à-dire :

a[start:stop:step]

est équivalent à :

a[slice(start, stop, step)]

Les objets Slice se comportent aussi légèrement différemment selon le nombre d'arguments, de la même manière que range(), c'est-à-dire que les deux slice(stop) et slice(start, stop[, step]) sont supportés. Pour ne pas spécifier un argument donné, on peut utiliser None, de sorte que, par exemple, a[start :] est équivalent à a[slice(start, None)] ou a[::-1] est équivalent à a[slice(None, None, -1)].

Bien que la notation basée sur : soit très utile pour le découpage simple, l'utilisation explicite des objets slice() simplifie la génération programmatique du découpage.

Commentaires (7)

Le [tutoriel Python][1] en parle (faites défiler la page jusqu'à la partie concernant le découpage en tranches).

Le diagramme ASCII est également utile pour se souvenir du fonctionnement des tranches :

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

Une façon de se souvenir du fonctionnement des tranches est de penser que les indices pointent entre les caractères, le bord gauche du premier caractère étant numéroté 0. Le bord droit du dernier caractère d'une chaîne de n caractères a donc l'indice n.

[1] : http://docs.python.org/tutorial/introduction.html#strings

Commentaires (2)

Enumération des possibilités permises par la grammaire :

>>> seq[:]                # [seq[0],   seq[1],          ..., seq[-1]    ]
>>> seq[low:]             # [seq[low], seq[low+1],      ..., seq[-1]    ]
>>> seq[:high]            # [seq[0],   seq[1],          ..., seq[high-1]]
>>> seq[low:high]         # [seq[low], seq[low+1],      ..., seq[high-1]]
>>> seq[::stride]         # [seq[0],   seq[stride],     ..., seq[-1]    ]
>>> seq[low::stride]      # [seq[low], seq[low+stride], ..., seq[-1]    ]
>>> seq[:high:stride]     # [seq[0],   seq[stride],     ..., seq[high-1]]
>>> seq[low:high:stride]  # [seq[low], seq[low+stride], ..., seq[high-1]]

Bien sûr, si (high-low)%stride != 0, alors le point final sera un peu plus bas que high-1.

Si stride est négatif, l'ordre est un peu modifié puisque nous comptons vers le bas :

>>> seq[::-stride]        # [seq[-1],   seq[-1-stride],   ..., seq[0]    ]
>>> seq[high::-stride]    # [seq[high], seq[high-stride], ..., seq[0]    ]
>>> seq[:low:-stride]     # [seq[-1],   seq[-1-stride],   ..., seq[low+1]]
>>> seq[high:low:-stride] # [seq[high], seq[high-stride], ..., seq[low+1]]

Les découpages étendus (avec virgules et ellipses) ne sont utilisés que par des structures de données spéciales (comme NumPy) ; les séquences de base ne les supportent pas.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
Commentaires (3)