Comprendre la notation des tranches

3287

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

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

Simon
la source

Réponses:

4509

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 stepvaleur, 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 :stopvaleur représente la première valeur qui n'est pas dans la tranche sélectionnée. Ainsi, la différence entre stopet startest le nombre d'éléments sélectionnés (si stepest 1, la valeur par défaut).

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

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, steppeut ê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 vous n'en demandez. Par exemple, si vous demandez a[:-2]et ane contient qu'un seul é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 se produire.

Relation avec l' slice()objet

L'opérateur de découpage []est actuellement utilisé dans le code ci-dessus avec un slice()objet utilisant la :notation (qui n'est valide qu'à l'intérieur []), c'est-à-dire:

a[start:stop:step]

est équivalent à:

a[slice(start, stop, step)]

Les objets Slice se comportent également légèrement différemment selon le nombre d'arguments, de la même manière que les range()deux slice(stop)et slice(start, stop[, step])sont pris en charge. Pour ignorer la spécification d'un argument donné, on pourrait utiliser None, de sorte que eg a[start:]soit équivalent à a[slice(start, None)]ou a[::-1]soit équivalent à a[slice(None, None, -1)].

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

Greg Hewgill
la source
122
Le découpage des types intégrés renvoie une copie mais ce n'est pas universel. Notamment, le découpage des tableaux NumPy renvoie une vue qui partage la mémoire avec l'original.
Beni Cherniavsky-Paskin
43
C'est une belle réponse avec les votes pour le prouver, mais il manque une chose: vous pouvez remplacer Nonen'importe lequel des espaces vides. Par exemple, [None:None]fait une copie entière. Ceci est utile lorsque vous devez spécifier la fin de la plage à l'aide d'une variable et devez inclure le dernier élément.
Mark Ransom
6
Ce qui m'agace vraiment, c'est que python dit que lorsque vous ne définissez pas le début et la fin, ils sont définis par défaut sur 0 et la longueur de la séquence. Ainsi, en théorie, lorsque vous utilisez "abcdef" [:: - 1], il doit être transformé en "abcdef" [0: 6: -1], mais ces deux expressions n'obtiennent pas la même sortie. Je sens que quelque chose manque dans la documentation python depuis la création du langage.
axell13
6
Et je sais que "abcdef" [:: - 1] est transformé en "abcdef" [6: -7: -1], donc la meilleure façon d'expliquer serait: soit len la longueur de la séquence. Si l'étape est positive , les valeurs par défaut pour le début et la fin sont 0 et len . Sinon, si l'étape est négative , les valeurs par défaut pour le début et la fin sont len et - len - 1.
axell13
4
Étant donné que stackoverflow.com/questions/39241529/… est marqué comme étant dupe de cela, serait-il logique d'ajouter une section avec ce que delfait la notation de tranche wrt. En particulier, ce del arr[:]n'est pas immédiatement évident ("arr [:] fait une copie, il en va de même pour supprimer cette copie ???" etc.)
khazhyk
538

Le tutoriel Python en parle (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 souvenir du fonctionnement des tranches consiste à considérer les index comme pointant entre les caractères, le bord gauche du premier caractère étant numéroté 0. Ensuite, le bord droit du dernier caractère d'une chaîne de n caractères a l'index n .

Hans Nowak
la source
10
Cette suggestion fonctionne pour une foulée positive, mais pas pour une foulée négative. D'après le diagramme, je m'attends a[-4,-6,-1]à l'être, yPmais ça l'est ty. Ce qui fonctionne toujours, c'est de penser dans les caractères ou les emplacements et d'utiliser l'indexation comme un intervalle semi-ouvert - ouvert à droite si foulée positive, ouvert à gauche si foulée négative.
aguadopd
Mais il n'y a aucun moyen de se réduire à un ensemble vide à partir de la fin (comme x[:0]pour le démarrage à partir du début), vous devez donc utiliser des petits tableaux dans des cas spéciaux. : /
endolith
412

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 inférieur à high-1.

Si strideest négatif, l'ordre est légèrement modifié depuis le décompte:

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

Le découpage étendu (avec des virgules et des ellipses) n'est principalement utilisé que par des structures de données spéciales (comme NumPy); les séquences de base ne les prennent pas en charge.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
éphémère
la source
En fait, il reste encore quelque chose, par exemple si je tape «pomme» [4: -4: -1] j'obtiens «elp», python traduit le -4 en 1 peut-être?
liyuan
notez que les backticks sont dépréciés au profit derepr
wjandrea
@liyuan Le type d'implémentation __getitem__est; votre exemple est équivalent à apple[slice(4, -4, -1)].
chepner
320

Les réponses ci-dessus ne traitent pas de l'affectation des tranches. Pour comprendre l'attribution des tranches, il est utile d'ajouter un autre concept à l'art ASCII:

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

>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
 'P'
>>> p[5]
 'n'

# Slicing gives lists
>>> p[0:1]
 ['P']
>>> p[0:2]
 ['P','y']

Une heuristique est, pour une tranche de zéro à n, de penser: "zéro est le début, commencez par le début et prenez n éléments dans une liste".

>>> p[5] # the last of six items, indexed from zero
 'n'
>>> p[0:5] # does NOT include the last item!
 ['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
 ['P','y','t','h','o','n']

Une autre heuristique est, "pour n'importe quelle tranche, remplacez le début par zéro, appliquez l'heuristique précédente pour obtenir la fin de la liste, puis comptez le premier nombre pour remonter les éléments du début"

>>> p[0:4] # Start at the beginning and count out 4 items
 ['P','y','t','h']
>>> p[1:4] # Take one item off the front
 ['y','t','h']
>>> p[2:4] # Take two items off the front
 ['t','h']
# etc.

La première règle d'affectation de tranche est que, puisque le découpage renvoie une liste, l'attribution de tranche nécessite une liste (ou autre itérable):

>>> p[2:3]
 ['t']
>>> p[2:3] = ['T']
>>> p
 ['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

La deuxième règle d'affectation de tranche, que vous pouvez également voir ci-dessus, est que quelle que soit la partie de la liste renvoyée par l'indexation de tranche, c'est la même partie qui est modifiée par l'affectation de tranche:

>>> p[2:4]
 ['T','h']
>>> p[2:4] = ['t','r']
>>> p
 ['P','y','t','r','o','n']

La troisième règle d'attribution de tranche est que la liste assignée (itérable) n'a pas à avoir la même longueur; la tranche indexée est simplement découpée et remplacée en masse par ce qui est attribué:

>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
 ['P','y','s','p','a','m','o','n']

La partie la plus délicate à laquelle s'habituer est l'affectation aux tranches vides. En utilisant l'heuristique 1 et 2, il est facile de se familiariser avec l' indexation d' une tranche vide:

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []

Et puis, une fois que vous avez vu cela, l'affectation des tranches à la tranche vide est également logique:

>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
 ['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
 ['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
 ['P','y','t','h','x','y','o','n'] # The result is longer still

Notez que, comme nous ne modifions pas le deuxième numéro de la tranche (4), les éléments insérés s'empilent toujours juste contre le «o», même lorsque nous les affectons à la tranche vide. Ainsi, la position pour l'affectation de tranche vide est l'extension logique des positions pour les affectations de tranche non vide.

Sauvegarde un peu, que se passe-t-il lorsque vous continuez avec notre procession de comptage de la tranche commençant?

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []

Avec le tranchage, une fois que vous avez terminé, vous avez terminé; il ne commence pas à trancher en arrière. En Python, vous n'obtenez pas de pas négatifs à moins que vous ne les demandiez explicitement en utilisant un nombre négatif.

>>> p[5:3:-1]
 ['n','o']

Il y a des conséquences étranges à la règle "une fois que vous avez terminé, vous avez terminé":

>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []
>>> p[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

En fait, par rapport à l'indexation, le découpage Python est étrangement à l'abri des erreurs:

>>> p[100:200]
 []
>>> p[int(2e99):int(1e99)]
 []

Cela peut parfois être utile, mais cela peut également conduire à un comportement quelque peu étrange:

>>> p
 ['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
 ['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']

Selon votre candidature, cela pourrait ... ou non ... être ce que vous espériez!


Voici le texte de ma réponse originale. Il a été utile à beaucoup de gens, donc je ne voulais pas le supprimer.

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

David M. Perlman
la source
246

Expliquer la notation de tranche de Python

En bref, les points ( :) dans la notation souscrite ( subscriptable[subscriptarg]) marque la notation tranche - qui a les arguments optionnels, start, stop, step:

sliceable[start:stop:step]

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

Définitions importantes

Pour commencer, définissons quelques termes:

start: l'index de début de la tranche, il inclura l'élément à cet index sauf s'il est identique à stop , par défaut à 0, c'est-à-dire le premier index. S'il est négatif, cela signifie commencer les narticles à partir de la fin.

stop: l'index de fin de la tranche, il n'inclut pas l'élément à cet index, par défaut la longueur de la séquence découpée, c'est-à-dire jusqu'à et y compris la fin.

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

Fonctionnement de 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 en arrière à partir de la fin pour le début et l' arrêt , et pour l' étape , vous décrémentez simplement votre index. Cet exemple provient du didacticiel 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 tranchage

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 (qui implémentent en__getitem__ fait la 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 existe des valeurs par défaut pour le démarrage , l' arrêt et l' étape , donc pour accéder aux valeurs par défaut, laissez simplement de côté l'argument.

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

my_list[-9:]

Quand je vois cela, j'ai lu la partie entre parenthèses comme "9ème de la fin à la fin". (En fait, je l'abrège mentalement en "-9, sur")

Explication:

La notation complète est

my_list[-9:None:None]

et pour remplacer les valeurs par défaut (en fait, quand stepest négatif, stopla valeur par défaut est -len(my_list) - 1, donc Nonepour stop signifie simplement qu'il va à l'étape de fin qui le prend):

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

Les deux points , :c'est ce qui indique à Python que vous lui donnez une tranche et non un index régulier. C'est pourquoi la façon idiomatique de faire une copie superficielle des listes en Python 2 est

list_copy = sequence[:]

Et les effacer c'est avec:

del my_list[:]

(Python 3 obtient une méthode list.copyet list.clear.)

Quand stepest négatif, les valeurs par défaut startet stopchangent

Par défaut, lorsque l' stepargument est vide (ou None), il est affecté à +1.

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

Ainsi, une tranche négative changera les valeurs par défaut pour startet stop!

Confirmer cela dans la source

J'aime encourager les utilisateurs à lire la source ainsi que la documentation. Le code source des objets tranche et cette logique se trouve ici . On détermine d'abord si stepest négatif:

 step_is_negative = step_sign < 0;

Si c'est le cas, la borne inférieure -1 signifie que nous coupons jusqu'au début et y compris, et la borne supérieure est la longueur moins 1, ce qui signifie que nous commençons à la fin. (Notez que la sémantique de ceci -1est différente de -1celle selon laquelle les utilisateurs peuvent passer 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;
}

Sinon, il stepest positif, et la limite inférieure sera nulle et la limite supérieure (jusqu'à laquelle nous montons, sans inclure) la longueur de la liste découpée en tranches.

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

Ensuite, nous devrons peut-être appliquer les valeurs par défaut pour startet stop- la valeur par défaut alors pour startest calculée comme la limite supérieure lorsque stepest négatif:

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

et stop, la borne inférieure:

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

Donnez à vos tranches un nom descriptif!

Il peut être utile de séparer la formation de la tranche de sa transmission à la list.__getitem__méthode ( c'est ce que font les crochets ). Même si vous n'êtes pas nouveau, cela permet à votre code d'être plus lisible afin que les autres personnes susceptibles de lire votre code puissent 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 tranche:

last_nine_slice = slice(-9, None)

Le deuxième argument,, Noneest requis, de sorte que le premier argument soit interprété comme l' startargument sinon ce serait l' stopargument .

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 de noter que les plages prennent également des tranches:

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

Considérations sur la mémoire:

Étant donné que des tranches de listes Python créent de nouveaux objets en mémoire, une autre fonction importante à prendre en compte est itertools.islice. En règle générale, vous souhaiterez parcourir une tranche, pas seulement la créer statiquement en mémoire. isliceest parfait pour ça. Une mise en garde, il ne prend pas en charge les arguments négatifs pour start, stopou 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 tranches de liste effectuent une copie est une caractéristique des listes elles-mêmes. Si vous découpez des objets avancés comme un Pandas DataFrame, il peut retourner une vue sur l'original, et non une copie.

Aaron Hall
la source
147

Et quelques choses qui n'étaient pas immédiatement évidentes pour moi lorsque j'ai vu la syntaxe de découpage pour la première fois:

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

Un moyen facile d'inverser des séquences!

Et si vous vouliez, pour une raison quelconque, un élément sur deux dans l'ordre inverse:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]
Dana
la source
100

En Python 2.7

Découpage 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).

Comprendre l'attribution d'index est très important.

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

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

Lorsque 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 (à l'exclusion de l'élément à l'indice bth). Utilisez la règle d'indexation ci-dessus et n'oubliez pas 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 plage continue indéfiniment dans les deux sens:

...,-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 pendant que vous parcourez en utilisant les règles pour a, b, c ci-dessus, vous obtiendrez soit une liste d'éléments (touchés pendant la traversée), soit 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]
Ankur Agarwal
la source
2
un autre exemple intéressant: a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2]qui se traduit par[9]
Deviacium
96

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)
AdrianoFerrari
la source
65

Après l'avoir un peu utilisé, je me rends compte que la description la plus simple est qu'elle est exactement la même que les arguments d'une forboucle ...

(from:to:step)

Chacun d'eux est facultatif:

(:to:step)
(from::step)
(from:to)

Ensuite, l'indexation négative a juste besoin de vous pour ajouter la longueur de la chaîne aux indices négatifs pour la comprendre.

Cela fonctionne pour moi de toute façon ...

Simon
la source
52

Je trouve plus facile de me rappeler comment cela fonctionne, puis je peux trouver une combinaison spécifique de démarrage / arrêt / étape.

Il est instructif de comprendre d' range()abord:

def range(start=0, stop, step=1):  # Illegal syntax, but that's the effect
    i = start
    while (i < stop if step > 0 else i > stop):
        yield i
        i += step

Commencez par start, augmentez de step, n'atteignez pas stop. Très simple.

La chose à retenir à propos de l'étape négative est que stopc'est toujours la fin exclue, qu'elle soit supérieure ou inférieure. Si vous voulez la même tranche dans l'ordre inverse, il est beaucoup plus propre de faire l'inversion séparément: par exemple, 'abcde'[1:-2][::-1]tranche un caractère de gauche, deux de droite, puis inverse. (Voir aussi reversed().)

Le découpage de séquence est le même, sauf qu'il normalise d'abord les indices négatifs et qu'il ne peut jamais sortir de la séquence:

TODO : Le code ci-dessous avait un bug avec "ne jamais sortir de la séquence" lorsque abs (étape)> 1; Je pense que je l'ai corrigé pour être correct, mais c'est difficile à comprendre.

def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
    if start is None:
        start = (0 if step > 0 else len(seq)-1)
    elif start < 0:
        start += len(seq)
    if not 0 <= start < len(seq):  # clip if still outside bounds
        start = (0 if step > 0 else len(seq)-1)
    if stop is None:
        stop = (len(seq) if step > 0 else -1)  # really -1, not last element
    elif stop < 0:
        stop += len(seq)
    for i in range(start, stop, step):
        if 0 <= i < len(seq):
            yield seq[i]

Ne vous inquiétez pas des is Nonedétails - rappelez-vous simplement que l'omission startet / ou fait stoptoujours la bonne chose pour vous donner la séquence entière.

La normalisation des indices négatifs permet d'abord de compter indépendamment le début et / ou l'arrêt depuis la fin: 'abcde'[1:-2] == 'abcde'[1:3] == 'bc'malgré range(1,-2) == []. La normalisation est parfois considérée comme "modulo la longueur", mais notez qu'elle n'ajoute la longueur qu'une seule fois: par exemple, 'abcde'[-53:42]c'est juste la chaîne entière.

Beni Cherniavsky-Paskin
la source
3
Ce this_is_how_slicing_worksn'est pas la même chose que la tranche python. EG [0, 1, 2][-5:3:3]obtiendra [0] en python, mais list(this_is_how_slicing_works([0, 1, 2], -5, 3, 3))obtiendra [1].
Eastsun
@Eastsun Oups, vous avez raison! Un cas plus clair: range(4)[-200:200:3] == [0, 3]mais list(this_is_how_slicing_works([0, 1, 2, 3], -200, 200, 3)) == [2]. Mon if 0 <= i < len(seq):était une tentative d'implémenter "ne jamais sortir de la séquence" simplement mais est incorrect pour l'étape> 1. Je vais le réécrire plus tard dans la journée (avec des tests).
Beni Cherniavsky-Paskin
40

J'utilise la méthode "un index entre les éléments" pour y penser moi-même, mais une façon de le décrire qui aide parfois les autres à l'obtenir est la suivante:

mylist[X:Y]

X est l'indice du premier élément souhaité.
Y est l'indice du premier élément dont vous ne voulez pas .

Steve Losh
la source
40
Index:
      ------------>
  0   1   2   3   4
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
  0  -4  -3  -2  -1
      <------------

Slice:
    <---------------|
|--------------->
:   1   2   3   4   :
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
:  -4  -3  -2  -1   :
|--------------->
    <---------------|

J'espère que cela vous aidera à modéliser la liste en Python.

Référence: http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

xiaoyu
la source
38

Notation de découpage en python:

a[start:end:step]
  • Pour startet end, les valeurs négatives sont interprétées comme étant relatives à la fin de la séquence.
  • Les indices positifs pour endindiquent la position après le dernier élément à inclure.
  • Les valeurs vides sont initialisés comme suit: [+0:-0:1].
  • L'utilisation d'une étape négative inverse l'interprétation de startetend

La notation s'étend aux matrices (numpy) et aux tableaux multidimensionnels. Par exemple, pour découper des colonnes entières, vous pouvez utiliser:

m[::,0:2:] ## slice the first two columns

Les tranches contiennent des références, et non des copies, des éléments du tableau. Si vous souhaitez faire une copie séparée d'un tableau, vous pouvez utiliser deepcopy().

nobar
la source
34

Vous pouvez également utiliser l'affectation de tranche pour supprimer un ou plusieurs éléments d'une liste:

r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]
dansalmo
la source
33

Ceci est juste pour quelques informations supplémentaires ... Considérez la liste ci-dessous

>>> l=[12,23,345,456,67,7,945,467]

Quelques autres astuces pour inverser la liste:

>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
Arindam Roychowdhury
la source
33

Voici comment j'enseigne les tranches aux débutants:

Comprendre la différence entre l'indexation et le découpage:

Wiki Python a cette image étonnante qui distingue clairement l'indexation et le découpage.

Entrez la description de l'image ici

Il s'agit d'une liste contenant six éléments. Pour mieux comprendre le découpage, considérez cette liste comme un ensemble de six boîtes placées ensemble. Chaque boîte contient un alphabet.

L'indexation est comme traiter le contenu de la boîte. Vous pouvez vérifier le contenu de n'importe quelle boîte. Mais vous ne pouvez pas vérifier le contenu de plusieurs cases à la fois. Vous pouvez même remplacer le contenu de la boîte. Mais vous ne pouvez pas placer deux balles dans une boîte ou remplacer deux balles à la fois.

In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']

In [124]: alpha[0]
Out[124]: 'a'

In [127]: alpha[0] = 'A'

In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']

In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]

TypeError: list indices must be integers, not tuple

Le découpage, c'est comme gérer les boîtes elles-mêmes. Vous pouvez ramasser la première boîte et la placer sur une autre table. Pour récupérer la boîte, tout ce que vous devez savoir est la position de début et de fin de la boîte.

Vous pouvez même ramasser les trois premières cases ou les deux dernières cases ou toutes les cases entre 1 et 4. Ainsi, vous pouvez choisir n'importe quel ensemble de cases si vous connaissez le début et la fin. Ces positions sont appelées positions de départ et d'arrêt.

La chose intéressante est que vous pouvez remplacer plusieurs boîtes à la fois. Vous pouvez également placer plusieurs boîtes où vous le souhaitez.

In [130]: alpha[0:1]
Out[130]: ['A']

In [131]: alpha[0:1] = 'a'

In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']

In [133]: alpha[0:2] = ['A', 'B']

In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']

In [135]: alpha[2:2] = ['x', 'xx']

In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']

Découpage avec étape:

Jusqu'à présent, vous avez sélectionné des boîtes en continu. Mais parfois, vous devez décrocher discrètement. Par exemple, vous pouvez récupérer une boîte sur deux. Vous pouvez même ramasser une boîte sur trois à la fin. Cette valeur est appelée taille de pas. Cela représente l'écart entre vos micros successifs. La taille du pas doit être positive si vous choisissez des cases du début à la fin et vice versa.

In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [142]: alpha[1:5:2]
Out[142]: ['b', 'd']

In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']

In [144]: alpha[1:5:-2]
Out[144]: []

In [145]: alpha[-1:-5:2]
Out[145]: []

Comment Python détermine les paramètres manquants:

Lors du découpage, si vous omettez un paramètre, Python essaie de le comprendre automatiquement.

Si vous vérifiez le code source de CPython , vous trouverez une fonction appelée PySlice_GetIndicesEx () qui calcule les indices d'une tranche pour n'importe quel paramètre donné. Voici le code équivalent logique en Python.

Cette fonction prend un objet Python et des paramètres facultatifs pour le découpage et renvoie le début, l'arrêt, l'étape et la longueur de découpage pour le découpage demandé.

def py_slice_get_indices_ex(obj, start=None, stop=None, step=None):

    length = len(obj)

    if step is None:
        step = 1
    if step == 0:
        raise Exception("Step cannot be zero.")

    if start is None:
        start = 0 if step > 0 else length - 1
    else:
        if start < 0:
            start += length
        if start < 0:
            start = 0 if step > 0 else -1
        if start >= length:
            start = length if step > 0 else length - 1

    if stop is None:
        stop = length if step > 0 else -1
    else:
        if stop < 0:
            stop += length
        if stop < 0:
            stop = 0 if step > 0 else -1
        if stop >= length:
            stop = length if step > 0 else length - 1

    if (step < 0 and stop >= start) or (step > 0 and start >= stop):
        slice_length = 0
    elif step < 0:
        slice_length = (stop - start + 1)/(step) + 1
    else:
        slice_length = (stop - start - 1)/(step) + 1

    return (start, stop, step, slice_length)

C'est l'intelligence qui est présente derrière les tranches. Étant donné que Python a une fonction intégrée appelée tranche, vous pouvez passer certains paramètres et vérifier à quel point il calcule intelligemment les paramètres manquants.

In [21]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [22]: s = slice(None, None, None)

In [23]: s
Out[23]: slice(None, None, None)

In [24]: s.indices(len(alpha))
Out[24]: (0, 6, 1)

In [25]: range(*s.indices(len(alpha)))
Out[25]: [0, 1, 2, 3, 4, 5]

In [26]: s = slice(None, None, -1)

In [27]: range(*s.indices(len(alpha)))
Out[27]: [5, 4, 3, 2, 1, 0]

In [28]: s = slice(None, 3, -1)

In [29]: range(*s.indices(len(alpha)))
Out[29]: [5, 4]

Remarque: Cet article a été initialement écrit dans mon blog, The Intelligence Behind Python Slices .

ChillarAnand
la source
29

En règle générale, l'écriture de code avec de nombreuses valeurs d'index codées en dur conduit à un problème de lisibilité et de maintenance. Par exemple, si vous revenez au code un an plus tard, vous le regarderez et vous vous demanderez ce que vous pensiez quand vous l'avez écrit. La solution présentée est simplement un moyen d'indiquer plus clairement ce que fait réellement votre code. En général, la tranche intégrée () crée un objet tranche qui peut être utilisé partout où une tranche est autorisée. Par exemple:

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]

Si vous avez une instance de tranche s, vous pouvez obtenir plus d'informations à son sujet en consultant respectivement ses attributs s.start, s.stop et s.step. Par exemple:

>>> a = slice(10, 50, 2)
>>> a.start
10
>>> a.stop
50
>>> a.step
2
>>>
Python_Dude
la source
25

1. Notation de tranche

Pour faire simple, rappelez-vous que la tranche n'a qu'une seule forme:

s[start:end:step]

et voici comment cela fonctionne:

  • s: un objet qui peut être tranché
  • start: premier index pour démarrer l'itération
  • end: dernier index, NOTEZ que l' endindex ne sera pas inclus dans la tranche résultante
  • step: pick élément chaque stepindex

Une autre chose à l'importation: tous start, end, steppeut être omis! Et s'ils sont omis, leur valeur par défaut sera utilisée: 0,, len(s)en 1conséquence.

Les variations possibles sont donc:

# Mostly used variations
s[start:end]
s[start:]
s[:end]

# Step-related variations
s[:end:step]
s[start::step]
s[::step]

# Make a copy
s[:]

REMARQUE: Si start >= end(en tenant compte uniquement du moment step>0), Python renverra une tranche vide [].

2. Pièges

La partie ci-dessus explique les fonctionnalités de base sur le fonctionnement de la tranche, et cela fonctionnera dans la plupart des cas. Cependant, il peut y avoir des écueils à surveiller, et cette partie les explique.

Index négatifs

La toute première chose qui déroute les apprenants Python est qu'un indice peut être négatif! Pas de panique: un indice négatif signifie compter à rebours.

Par exemple:

s[-5:]    # Start at the 5th index from the end of array,
          # thus returning the last 5 elements.
s[:-5]    # Start at index 0, and end until the 5th index from end of array,
          # thus returning s[0:len(s)-5].

Étape négative

Rendre les choses plus confuses, cela steppeut aussi être négatif!

Une étape négative signifie itérer le tableau vers l'arrière: de la fin au début, avec l'index de fin inclus et l'index de début exclu du résultat.

REMARQUE : lorsque l'étape est négative, la valeur par défaut de startest len(s)(alors qu'elle endn'est pas égale à 0, car s[::-1]contient s[0]). Par exemple:

s[::-1]            # Reversed slice
s[len(s)::-1]      # The same as above, reversed slice
s[0:len(s):-1]     # Empty list

Erreur hors plage?

Soyez surpris: slice ne déclenche pas une IndexError lorsque l'index est hors de portée!

Si l'index est hors de portée, Python fera de son mieux pour définir l'index sur 0ou len(s)selon la situation. Par exemple:

s[:len(s)+5]      # The same as s[:len(s)]
s[-len(s)-5::]    # The same as s[0:]
s[len(s)+5::-1]   # The same as s[len(s)::-1], and the same as s[::-1]

3. Exemples

Terminons cette réponse avec des exemples, expliquant tout ce dont nous avons discuté:

# Create our array for demonstration
In [1]: s = [i for i in range(10)]

In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: s[2:]   # From index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]

In [4]: s[:8]   # From index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]

In [5]: s[4:7]  # From index 4 (included) up to index 7(excluded)
Out[5]: [4, 5, 6]

In [6]: s[:-2]  # Up to second last index (negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]

In [7]: s[-2:]  # From second last index (negative index)
Out[7]: [8, 9]

In [8]: s[::-1] # From last to first in reverse order (negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [9]: s[::-2] # All odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]

In [11]: s[-2::-2] # All even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]

In [12]: s[3:15]   # End is out of range, and Python will set it to len(s).
Out[12]: [3, 4, 5, 6, 7, 8, 9]

In [14]: s[5:1]    # Start > end; return empty list
Out[14]: []

In [15]: s[11]     # Access index 11 (greater than len(s)) will raise an IndexError
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]

IndexError: list index out of range
cizixs
la source
24

Les réponses précédentes ne traitent pas du découpage multidimensionnel de tableaux qui est possible en utilisant le célèbre package NumPy :

Le découpage peut également être appliqué à des tableaux multidimensionnels.

# Here, a is a NumPy array

>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>> a[:2, 0:3:2]
array([[1, 3],
       [5, 7]])

Le " :2" avant la virgule opère sur la première dimension et le " 0:3:2" après la virgule opère sur la deuxième dimension.

Statham
la source
4
Juste un rappel amical que vous ne pouvez pas faire cela sur Python listmais uniquement sur arrayNumpy
Mars Lee
15
#!/usr/bin/env python

def slicegraphical(s, lista):

    if len(s) > 9:
        print """Enter a string of maximum 9 characters,
    so the printig would looki nice"""
        return 0;
    # print " ",
    print '  '+'+---' * len(s) +'+'
    print ' ',
    for letter in s:
        print '| {}'.format(letter),
    print '|'
    print " ",; print '+---' * len(s) +'+'

    print " ",
    for letter in range(len(s) +1):
        print '{}  '.format(letter),
    print ""
    for letter in range(-1*(len(s)), 0):
        print ' {}'.format(letter),
    print ''
    print ''


    for triada in lista:
        if len(triada) == 3:
            if triada[0]==None and triada[1] == None and triada[2] == None:
                # 000
                print s+'[   :   :   ]' +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] == None and triada[2] != None:
                # 001
                print s+'[   :   :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] == None:
                # 010
                print s+'[   :{0:2d} :   ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] != None:
                # 011
                print s+'[   :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] == None:
                # 100
                print s+'[{0:2d} :   :   ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] != None:
                # 101
                print s+'[{0:2d} :   :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] == None:
                # 110
                print s+'[{0:2d} :{1:2d} :   ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] != None:
                # 111
                print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]

        elif len(triada) == 2:
            if triada[0] == None and triada[1] == None:
                # 00
                print s+'[   :   ]    ' + ' = ', s[triada[0]:triada[1]]
            elif triada[0] == None and triada[1] != None:
                # 01
                print s+'[   :{0:2d} ]    '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] == None:
                # 10
                print s+'[{0:2d} :   ]    '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] != None:
                # 11
                print s+'[{0:2d} :{1:2d} ]    '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]

        elif len(triada) == 1:
            print s+'[{0:2d} ]        '.format(triada[0]) + ' = ', s[triada[0]]


if __name__ == '__main__':
    # Change "s" to what ever string you like, make it 9 characters for
    # better representation.
    s = 'COMPUTERS'

    # add to this list different lists to experement with indexes
    # to represent ex. s[::], use s[None, None,None], otherwise you get an error
    # for s[2:] use s[2:None]

    lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]

    slicegraphical(s, lista)

Vous pouvez exécuter ce script et l'expérimenter, voici quelques exemples que j'ai obtenus du script.

  +---+---+---+---+---+---+---+---+---+
  | C | O | M | P | U | T | E | R | S |
  +---+---+---+---+---+---+---+---+---+
  0   1   2   3   4   5   6   7   8   9   
 -9  -8  -7  -6  -5  -4  -3  -2  -1 

COMPUTERS[ 4 : 7 ]     =  UTE
COMPUTERS[ 2 : 5 : 2 ] =  MU
COMPUTERS[-5 : 1 :-1 ] =  UPM
COMPUTERS[ 4 ]         =  U
COMPUTERS[-4 :-6 :-1 ] =  TU
COMPUTERS[ 2 :-3 : 1 ] =  MPUT
COMPUTERS[ 2 :-3 :-1 ] =  
COMPUTERS[   :   :-1 ] =  SRETUPMOC
COMPUTERS[-5 :   ]     =  UTERS
COMPUTERS[-5 : 0 :-1 ] =  UPMO
COMPUTERS[-5 :   :-1 ] =  UPMOC
COMPUTERS[-1 : 1 :-2 ] =  SEUM
[Finished in 0.9s]

Lorsque vous utilisez une étape négative, notez que la réponse est décalée vers la droite de 1.

mahmoh
la source
14

Mon cerveau semble heureux d'accepter qu'il lst[start:end]contient le start-ième élément. Je pourrais même dire que c'est une «hypothèse naturelle».

Mais parfois, un doute se glisse et mon cerveau demande à être rassuré qu'il ne contient pas le end-ème élément.

Dans ces moments, je me fie à ce théorème simple:

for any n,    lst = lst[:n] + lst[n:]

Cette jolie propriété m'indique qu'elle lst[start:end]ne contient pas le end-ème élément car il est dans lst[end:].

Notez que ce théorème est vrai pour tout n. Par exemple, vous pouvez vérifier que

lst = range(10)
lst[:-42] + lst[-42:] == lst

retourne True.

Robert
la source
12

À mon avis, vous comprendrez et mémoriserez mieux la notation de découpage de chaîne Python si vous la regardez de la manière suivante (lire la suite).

Travaillons avec la chaîne suivante ...

azString = "abcdefghijklmnopqrstuvwxyz"

Pour ceux qui ne le savent pas, vous pouvez créer n'importe quelle sous-chaîne en azStringutilisant la notationazString[x:y]

Venant d'autres langages de programmation, c'est là que le bon sens est compromis. Que sont x et y?

J'ai dû m'asseoir et exécuter plusieurs scénarios dans ma quête d'une technique de mémorisation qui m'aidera à me souvenir de ce que sont x et y et m'aidera à trancher correctement les chaînes à la première tentative.

Ma conclusion est que x et y doivent être considérés comme les indices limites qui entourent les chaînes que nous voulons ajouter. Nous devrions donc voir l'expression comme azString[index1, index2]ou encore plus clairement comme azString[index_of_first_character, index_after_the_last_character].

Voici un exemple de visualisation de cela ...

Letters   a b c d e f g h i j ...
         ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
             ┊           ┊
Indexes  0 1 2 3 4 5 6 7 8 9 ...
             ┊           ┊
cdefgh    index1       index2

Il vous suffit donc de définir index1 et index2 sur les valeurs qui entoureront la sous-chaîne souhaitée. Par exemple, pour obtenir la sous-chaîne "cdefgh", vous pouvez utiliser azString[2:8], car l'index sur le côté gauche de "c" est 2 et celui sur la bonne taille de "h" est 8.

N'oubliez pas que nous fixons les limites. Et ces limites sont les positions où vous pouvez placer des crochets qui seront enroulés autour de la sous-chaîne comme ceci ...

ab [ cdefgh ] ij

Cette astuce fonctionne tout le temps et est facile à mémoriser.

asiby
la source
11

La plupart des réponses précédentes clarifient les questions sur la notation des tranches.

La syntaxe d'indexation étendue utilisée pour le découpage est aList[start:stop:step], et les exemples de base sont:

Entrez la description de l'image ici:

Plus d'exemples de découpage: 15 tranches étendues

Roshan Bagdiya
la source
10

En Python, la forme la plus élémentaire de découpage est la suivante:

l[start:end]

lest une collection, startest un index inclusif et endest un index exclusif.

In [1]: l = list(range(10))

In [2]: l[:5] # First five elements
Out[2]: [0, 1, 2, 3, 4]

In [3]: l[-5:] # Last five elements
Out[3]: [5, 6, 7, 8, 9]

Lors du découpage depuis le début, vous pouvez omettre l'index zéro et lors du découpage jusqu'à la fin, vous pouvez omettre l'index final car il est redondant, alors ne soyez pas verbeux:

In [5]: l[:3] == l[0:3]
Out[5]: True

In [6]: l[7:] == l[7:len(l)]
Out[6]: True

Les entiers négatifs sont utiles lors des décalages par rapport à la fin d'une collection:

In [7]: l[:-1] # Include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]

In [8]: l[-3:] # Take the last three elements
Out[8]: [7, 8, 9]

Il est possible de fournir des indices hors limites lors du découpage tels que:

In [9]: l[:20] # 20 is out of index bounds, and l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [11]: l[-20:] # -20 is out of index bounds, and l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Gardez à l'esprit que le résultat du découpage d'une collection est une toute nouvelle collection. En outre, lorsque vous utilisez la notation de tranche dans les affectations, la longueur des affectations de tranche n'a pas besoin d'être la même. Les valeurs avant et après la tranche affectée seront conservées et la collection se réduira ou s'agrandira pour contenir les nouvelles valeurs:

In [16]: l[2:6] = list('abc') # Assigning fewer elements than the ones contained in the sliced collection l[2:6]

In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]

In [18]: l[2:5] = list('hello') # Assigning more elements than the ones contained in the sliced collection l [2:5]

In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]

Si vous omettez l'index de début et de fin, vous ferez une copie de la collection:

In [14]: l_copy = l[:]

In [15]: l == l_copy and l is not l_copy
Out[15]: True

Si les index de début et de fin sont omis lors de l'exécution d'une opération d'affectation, tout le contenu de la collection sera remplacé par une copie de ce qui est référencé:

In [20]: l[:] = list('hello...')

In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']

Outre le découpage de base, il est également possible d'appliquer la notation suivante:

l[start:end:step]

lest une collection, startest un index inclusif, endest un index exclusif et stepest une foulée qui peut être utilisée pour prendre chaque nième élément l.

In [22]: l = list(range(10))

In [23]: l[::2] # Take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]

In [24]: l[1::2] # Take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]

L'utilisation stepfournit une astuce utile pour inverser une collection en Python:

In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Il est également possible d'utiliser des entiers négatifs pour stepl'exemple suivant:

In[28]:  l[::-2]
Out[28]: [9, 7, 5, 3, 1]

Cependant, l'utilisation d'une valeur négative pour steppourrait devenir très déroutante. De plus, afin d'être Pythonic , vous devriez éviter d' utiliser start, endet stepen une seule tranche. Dans le cas où cela est nécessaire, envisagez de le faire en deux affectations (une pour découper et l'autre pour avancer).

In [29]: l = l[::2] # This step is for striding

In [30]: l
Out[30]: [0, 2, 4, 6, 8]

In [31]: l = l[1:-1] # This step is for slicing

In [32]: l
Out[32]: [2, 4, 6]
lmiguelvargasf
la source
10

Je veux en ajouter un Hello, World! exemple qui explique les bases des tranches pour les tout débutants. Cela m'a beaucoup aidé.

Ayons une liste avec six valeurs ['P', 'Y', 'T', 'H', 'O', 'N']:

+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
  0   1   2   3   4   5

Maintenant, les tranches les plus simples de cette liste sont ses sous-listes. La notation est [<index>:<index>]et la clé est de la lire comme ceci:

[ start cutting before this index : end cutting before this index ]

Maintenant, si vous faites une tranche [2:5]de la liste ci-dessus, cela se produira:

        |           |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
  0   1 | 2   3   4 | 5

Vous avez fait une coupe avant l'élément avec index 2et une autre coupe avant l'élément avec index 5. Le résultat sera donc une tranche entre ces deux coupes, une liste ['T', 'H', 'O'].

Jeyekomon
la source
10

Personnellement, j'y pense comme une forboucle:

a[start:end:step]
# for(i = start; i < end; i += step)

Notez également que les valeurs négatives pour startet endsont relatives à la fin de la liste et calculées dans l'exemple ci-dessus par given_index + a.shape[0].

Raman
la source
8

Ce qui suit est l'exemple d'un index d'une chaîne:

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

str="Name string"

Exemple de découpage: [début: fin: étape]

str[start:end] # Items start through end-1
str[start:]    # Items start through the rest of the array
str[:end]      # Items from the beginning through end-1
str[:]         # A copy of the whole array

Voici l'exemple d'utilisation:

print str[0] = N
print str[0:2] = Na
print str[0:7] = Name st
print str[0:7:2] = Nm t
print str[0:-1:2] = Nm ti
Prince Dhadwal
la source
5

Si vous pensez que les indices négatifs dans le découpage prêtent à confusion, voici une façon très simple d'y penser: remplacez simplement l'indice négatif par len - index. Par exemple, remplacez -3 par len(list) - 3.

La meilleure façon d'illustrer ce que le découpage fait en interne est simplement de le montrer dans du code qui implémente cette opération:

def slice(list, start = None, end = None, step = 1):
  # Take care of missing start/end parameters
  start = 0 if start is None else start
  end = len(list) if end is None else end

  # Take care of negative start/end parameters
  start = len(list) + start if start < 0 else start
  end = len(list) + end if end < 0 else end

  # Now just execute a for-loop with start, end and step
  return [list[i] for i in range(start, end, step)]
Shital Shah
la source
4

La technique de découpage de base consiste à définir le point de départ, le point d'arrêt et la taille du pas - également connu sous le nom de foulée.

Tout d'abord, nous allons créer une liste de valeurs à utiliser dans notre découpage.

Créez deux listes à découper. Le premier est une liste numérique de 1 à 9 (liste A). Le second est également une liste numérique, de 0 à 9 (liste B):

A = list(range(1, 10, 1)) # Start, stop, and step
B = list(range(9))

print("This is List A:", A)
print("This is List B:", B)

Indexez le numéro 3 de A et le numéro 6 de B.

print(A[2])
print(B[6])

Tranchage de base

La syntaxe d'indexation étendue utilisée pour le découpage est aList [start: stop: step]. L'argument start et l'argument step sont tous les deux par défaut sur none - le seul argument requis est stop. Avez-vous remarqué que cela est similaire à la façon dont la plage a été utilisée pour définir les listes A et B? En effet, l'objet tranche représente l'ensemble des indices spécifiés par plage (démarrage, arrêt, étape). Documentation Python 3.4.

Comme vous pouvez le voir, définir uniquement stop renvoie un élément. Étant donné que le démarrage par défaut est none, cela se traduit par la récupération d'un seul élément.

Il est important de noter que le premier élément est l'index 0, pas l' index 1. C'est pourquoi nous utilisons 2 listes pour cet exercice. Les éléments de la liste A sont numérotés en fonction de la position ordinale (le premier élément est 1, le deuxième élément est 2, etc.) tandis que les éléments de la liste B sont les nombres qui seraient utilisés pour les indexer ([0] pour le premier élément 0, etc.).

Avec la syntaxe d'indexation étendue, nous récupérons une plage de valeurs. Par exemple, toutes les valeurs sont récupérées avec deux points.

A[:]

Pour récupérer un sous-ensemble d'éléments, les positions de départ et d'arrêt doivent être définies.

Étant donné le modèle aList [start: stop], récupérez les deux premiers éléments de la liste A.

Babu Chandermani
la source
3

Je ne pense pas que le tutoriel Python diagramme du (cité dans diverses autres réponses) soit bon car cette suggestion fonctionne pour une foulée positive, mais pas pour une foulée négative.

Voici le schéma:

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

D'après le diagramme, je m'attends a[-4,-6,-1]à l'être, yPmais ça l'est ty.

>>> a = "Python"
>>> a[2:4:1] # as expected
'th'
>>> a[-4:-6:-1] # off by 1
'ty'

Ce qui fonctionne toujours, c'est de penser en caractères ou en emplacements et d'utiliser l'indexation comme un intervalle semi-ouvert - ouvert à droite si foulée positive, ouvert à gauche si foulée négative.

De cette façon, je peux penser a[-4:-6:-1]que a(-6,-4]dans la terminologie d' intervalle.

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

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