web-dev-qa-db-fra.com

Erreur de liste Python: étape [:: - 1] sur la tranche [: -1]

Je pensais comprendre les bases du découpage de liste en python, mais je recevais une erreur inattendue lors de l'utilisation d'un pas négatif sur une tranche, comme suit:

>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-1:-1]
[]

(Notez que cela est en cours d'exécution dans Python 3.5)

Pourquoi un [: - 1: -1] ne passe-t-il pas en arrière dans la tranche a [: - 1] de la même manière que dans toute la liste avec un [:: - 1]? 

Je me rends compte que vous pouvez aussi utiliser list.reverse (), mais essayez de mieux comprendre la fonctionnalité de slice sous-jacente de python. 

16
Matt Kelty

Le premier -1 dans a[:-1:-1] ne signifie pas ce que vous pensez qu'il fait.

Lors du découpage, les indices de début/fin négatifs ne sont pas interprétés littéralement. Au lieu de cela, ils sont utilisés pour se référer facilement à la fin de la liste (c'est-à-dire qu'ils sont relatifs à len(a)). Cela se produit indépendamment de la direction du découpage.

Cela signifie que

a[:-1:-1]

est équivalent à

a[:len(a)-1:-1]

Lorsqu’il est omis lors du découpage inversé, l’index de départ est défini par défaut sur len(a)-1, ce qui rend l’équivalent ci-dessus équivalent

a[len(a)-1:len(a)-1:-1]

Cela donne toujours une liste vide, puisque les index de début et de fin sont identiques et que l'index de fin est exclusif.

Pour découper en sens inverse jusqu’à l’élément zeroth inclus, vous pouvez utiliser l’une des notations suivantes:

>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:None:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-len(a)-1:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
22
NPE

Lorsque vous tapez [1, 2, 3, ...][1:4:1], il est identique à [1, 2, 3, ...][slice(1, 4, 1)]. Donc, 1:4:1 est l’abréviation de l’objet slice. slice signature est slice(stop) ou slice(start, stop[, step]) et vous pouvez également utiliser None pour les arguments.

:: -> slice(None, None, None)
:4 -> slice(4)
# and so on

Supposons que nous ayons [a: b: c]. Règles pour les index seront comme suit: 

  1. La première c est cochée. La valeur par défaut est +1, le signe c indique le sens en avant ou en arrière du pas. La valeur absolue de c indique la taille de l'étape. 
  2. Than a est coché. Lorsque c est positif ou None, la valeur par défaut de a est 0. Lorsque c est négatif, la valeur par défaut de a est -1.
  3. Finalement, b est coché. Lorsque c est positif ou None, la valeur par défaut de b est len. Lorsque c est négatif, la valeur par défaut de b est -(len+1).

Note 1: Les tranches dégénérées en Python sont traitées avec élégance: 

  • l'index trop grand ou trop petit est remplacé par len ou 0
  • une limite supérieure plus petite que la limite inférieure renvoie une liste ou une chaîne vide ou autre chose (pour positive c). 

Note 2: En gros, Python récupère des éléments alors que cette condition (a < b) if (c > 0) else (a > b) est True (mise à jour a += c à chaque étape). De plus, tous les index négatifs sont remplacés par len - index.

Si vous combinez ces règles et vos notes, vous comprendrez pourquoi vous avez une liste vide. Dans ton cas:

 In[1]: [1, 2, 3, 4, 5, 6][:-1:-1]        # `c` is negative so `a` is -1 and `b` is -1
Out[1]: [] 

# it is the same as:

 In[2]: [1, 2, 3, 4, 5, 6][-1: -1: -1]    # which will produce you an empty list 
Out[2]: [] 

Il existe une très bonne discussion sur la notation de tranche: Expliquez la notation de tranche de Python !

8
godaygo

Je trouve généralement utile de découper un objet range- (cela n’est possible que dans python3 - en python2 range produit une list et xrange ne peut pas être découpé en tranches) si j’ai besoin de savoir quels index sont utilisés pour une liste de longueur donnée:

>>> range(10)[::-1]  
range(9, -1, -1)

>>> range(10)[:-1]  
range(0, 9)

Et dans votre dernier cas:

>>> range(10)[:-1:-1]
range(9, 9, -1)

Cela explique aussi ce qui s'est passé. Le premier index est 9, mais 9 n'est pas inférieur à l'index d'arrêt 9 (notez qu'en python, l'index d'arrêt est exclu ), de sorte qu'il s'arrête sans donner d'élément.

Notez que l'indexation peut également être appliquée de manière séquentielle:

>>> list(range(10))[::-1][:-1]  # first reverse then exclude last item.
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> list(range(10))[:-1][::-1]  # other way around
[8, 7, 6, 5, 4, 3, 2, 1, 0]
4
MSeifert

slice fonctionne de manière similaire à range en ce que, lorsque vous définissez l'argument step en nombre négatif, les arguments start et stop fonctionnent dans le sens opposé.

>>> list(range(9, -1, -1)) == a[::-1]
True

Quelques exemples peuvent aider à rendre cela plus clair:

>>> a[6:2:-2]
[6, 4]
>>> a[0:None:1] == a[::]
True
>>> a[-1:None:-1] == a[::-1]
True
>>> a[-2:None:-1] == a[:-1][::-1]
True
1
John B