Question Comprendre la notation de tranche de Python


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

Pour moi, cette notation a besoin d'un peu de récupération.

Cela a l'air extrêmement puissant, mais je n'ai pas vraiment compris.


2290
2018-02-03 22:31


origine


Réponses:


C'est vraiment simple:

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

Il y a aussi step valeur, qui peut être utilisé avec l'un des éléments ci-dessus:

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

Le point clé à retenir est que le :end valeur représente la première valeur qui est ne pas dans la tranche sélectionnée. Donc, la différence entre end 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 end peut-être un négatif nombre, ce qui signifie qu'il compte à partir de la fin du tableau au lieu du début. Alors:

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 a contient seulement un élément, vous obtenez une liste vide au lieu d'une erreur. Parfois, vous préférez l'erreur, vous devez donc être conscient que cela peut arriver.


3094
2018-02-03 22:48



le Didacticiel Python parle à ce sujet (faites défiler un peu jusqu'à ce que vous arriviez à la partie sur le découpage).

Le diagramme d'art 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 rappeler comment fonctionnent les tranches est de penser aux indices comme pointant entre caractères, avec le bord gauche du premier caractère numéroté 0. Puis le bord droit du dernier caractère d'une chaîne de caractères n les caractères ont un index n.


394
2018-02-03 22:49



Enumérant les 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, la commande est un peu modifiée puisque nous comptons:

>>> 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 des virgules et des ellipses) sont principalement utilisés uniquement 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 `item`
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'

310
2018-02-03 23:08



Les réponses ci-dessus ne traitent pas de l'attribution de tranches:

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

Cela peut également clarifier la différence entre le découpage et l'indexation.


199
2018-01-18 21:37



Expliquer la notation de tranche de Python

En bref, les deux points (:) en notation d'indice (subscriptable[subscriptarg]) faire la notation de tranche - qui a les arguments facultatifs, start, stop, step:

sliceable[start:stop:step]

Le découpage Python est un moyen informatique rapide d'accéder méthodiquement à certaines parties de vos données. À mon avis, pour être même un programmeur Python intermédiaire, c'est un aspect du langage qu'il faut connaître.

Définitions importantes

Pour commencer, définissons quelques termes:

début: l'indice de début de la tranche, il inclura l'élément à cet indice sauf s'il est le même que Arrêtez, par défaut à 0, c'est-à-dire le premier index. Si c'est négatif, cela signifie commencer n articles de la fin.

Arrêtez: l'indice de fin de la tranche, il fait ne pas inclure l'élément à cet index, par défaut à la longueur de la séquence en tranches, c'est-à-dire, jusqu'à et y compris la fin.

étape: le montant par lequel l'indice augmente, par défaut à 1. S'il est négatif, vous tranchez sur l'itérable à l'envers.

Comment fonctionne l'indexation

Vous pouvez faire n'importe lequel de ces nombres positifs ou négatifs. La signification des nombres positifs est simple, mais pour les nombres négatifs, tout comme les index en Python, vous comptez à rebours à partir de la fin pour le début et Arrêtezet pour le étape, vous décrémentez simplement votre index. Cet exemple est du tutoriel de la documentation, mais je l'ai légèrement modifié pour indiquer quel élément d'une séquence chaque index fait référence:

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

Comment fonctionne le découpage

Pour utiliser la notation de tranche avec une séquence qui la prend en charge, vous devez inclure au moins un deux-points dans les crochets qui suivent la séquence (ce qui en fait mettre en œuvre le __getitem__ méthode de la séquence, selon le modèle de données Python.)

La notation de tranche fonctionne comme ceci:

sequence[start:stop:step]

Et rappelez-vous qu'il y a des défauts pour début, Arrêtez, et étape, donc pour accéder aux valeurs par défaut, il suffit d'omettre l'argument.

La notation de tranche pour obtenir les neuf derniers éléments d'une liste (ou toute autre séquence qui la supporte, comme une chaîne) ressemblerait à ceci:

my_list[-9:]

Quand je vois cela, j'ai lu la partie entre parenthèses comme "9e à la fin, jusqu'à la fin". (En fait, je l'abréger mentalement comme "-9, sur")

Explication:

La notation complète est

my_list[-9:None:None]

et de substituer les défauts (en fait quand step est négatif, stoppar défaut est -len(my_list) - 1, alors None pour stop vraiment signifie qu'il va à n'importe quelle étape de fin le prend pour):

my_list[-9:len(my_list):1]

le côlon, :, c'est ce qui indique à Python que vous lui donnez une tranche et non un index régulier. C'est pourquoi la manière idiomatique de faire une copie superficielle des listes dans Python 2 est

list_copy = sequence[:]

Et les effacer est avec:

del my_list[:]

(Python 3 obtient un list.copy et list.clear méthode.)

Quand step est négatif, les valeurs par défaut pour start et stop changement

Par défaut, lorsque le step argument est vide (ou None), il est affecté à +1.

Mais vous pouvez passer un nombre entier négatif, et la liste (ou la plupart des autres articles standards) sera découpée de la fin au début.

Ainsi, une tranche négative va changer les valeurs par défaut pour start et stop!

Confirmant cela dans la source

J'aime encourager les utilisateurs à lire la source ainsi que la documentation. le code source pour les objets tranche et cette logique se trouve ici. Nous déterminons d'abord si step est négatif:

 step_is_negative = step_sign < 0;

Si oui, la limite inférieure est -1  ce qui signifie que nous découpons tout le chemin jusqu'au début, et la limite supérieure est la longueur moins 1, ce qui signifie que nous commençons à la fin. (Notez que la sémantique de cette -1 est différent de -1 que les utilisateurs peuvent transmettre des index en Python indiquant le dernier élément.)

if (step_is_negative) {
    lower = PyLong_FromLong(-1L);
    if (lower == NULL)
        goto error;

    upper = PyNumber_Add(length, lower);
    if (upper == NULL)
        goto error;
}

Autrement step est positif, et la limite inférieure sera zéro et la limite supérieure (que nous n'allons pas inclure) de la longueur de la liste tranchée.

else {
    lower = _PyLong_Zero;
    Py_INCREF(lower);
    upper = length;
    Py_INCREF(upper);
}

Ensuite, nous pouvons avoir besoin d'appliquer les valeurs par défaut pour start et stop - La valeur par défaut alors pour start est calculé comme la limite supérieure lorsque step est négatif:

if (self->start == Py_None) {
    start = step_is_negative ? upper : lower;
    Py_INCREF(start);
}

et stop, la limite inférieure:

if (self->stop == Py_None) {
    stop = step_is_negative ? lower : upper;
    Py_INCREF(stop);
}

Donnez à vos tranches un nom descriptif!

Vous pouvez trouver utile de séparer la formation de la tranche de passer à la list.__getitem__ méthode (c'est ce que font les crochets). Même si vous n'êtes pas novice, cela rend votre code plus lisible, de sorte que les autres utilisateurs qui doivent lire votre code peuvent plus facilement comprendre ce que vous faites.

Cependant, vous ne pouvez pas simplement affecter des entiers séparés par des deux-points à une variable. Vous devez utiliser l'objet slice:

last_nine_slice = slice(-9, None)

Le deuxième argument, None, est requis, de sorte que le premier argument est interprété comme le start argument sinon ce serait le stop argument.

Vous pouvez ensuite passer l'objet tranche à votre séquence:

>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]

Il est intéressant que les gammes prennent aussi des tranches:

>>> range(100)[last_nine_slice]
range(91, 100)

Considérations de mémoire:

Puisque les tranches de listes Python créent de nouveaux objets en mémoire, une autre fonction importante à prendre en compte est itertools.islice. Généralement, vous souhaiterez parcourir une section, pas seulement la créer statiquement en mémoire. islice est parfait pour ça. Une mise en garde, il ne prend pas en charge les arguments négatifs à start, stop, ou step, donc si c'est un problème, vous devrez peut-être calculer des indices ou inverser l'itérable à l'avance.

length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)

et maintenant:

>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]

Le fait que les coupes de liste font une copie est une caractéristique des listes elles-mêmes. Si vous découpez des objets avancés comme un DataFrame Pandas, il peut retourner une vue sur l'original, et non sur une copie.


184
2017-07-12 13:19



Et quelques choses qui n'étaient pas immédiatement évidentes pour moi quand j'ai vu la syntaxe de découpage:

>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]

Un moyen facile d'inverser les séquences!

Et si vous vouliez, pour une raison quelconque, chaque deuxième élément de la séquence inversée:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]

124
2018-02-03 23:15



Trouvé cette grande table à http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)

84
2017-09-06 06:50



En Python 2.7

Trancher en Python

[a:b:c]

len = length of string, tuple or list

c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.

a --  When c is positive or blank, default is 0. When c is negative, default is -1.

b --  When c is positive or blank, default is len. When c is negative, default is -(len+1).

La compréhension de l'affectation d'index est très importante.

In forward direction, starts at 0 and ends at len-1

In backward direction, starts at -1 and ends at -len

Quand vous dites [a: b: c], vous dites en fonction du signe de c (en avant ou en arrière), commencez en a et terminez en b (excluant l'élément en indice b). Utilisez la règle d'indexation ci-dessus et rappelez-vous que vous ne trouverez que des éléments dans cette plage:

-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1

Mais cette gamme continue indéfiniment dans les deux directions:

...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....

Par exemple:

             0    1    2   3    4   5   6   7   8   9   10   11
             a    s    t   r    i   n   g
    -9  -8  -7   -6   -5  -4   -3  -2  -1

Si votre choix de a, b et c permet de chevaucher la plage ci-dessus lorsque vous traversez les règles pour a, b, c ci-dessus, vous obtiendrez une liste avec des éléments (touchés pendant la traversée) ou vous obtiendrez une liste vide.

Une dernière chose: si a et b sont égaux, alors vous obtenez également une liste vide:

>>> l1
[2, 3, 4]

>>> l1[:]
[2, 3, 4]

>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]

>>> l1[:-4:-1] # a default is -1
[4, 3, 2]

>>> l1[:-3:-1] # a default is -1
[4, 3]

>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]

>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]


>>> l1[-100:-200:-1] # Interesting
[]

>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]


>>> l1[-1:-1:1]
[]


>>> l1[-1:5:1] # Interesting
[4]


>>> l1[1:-7:1]
[]

>>> l1[1:-7:-1] # Interesting
[3, 2]

>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]

84
2017-10-22 05:33