So funktioniert das Slicing in Python

Lesezeit: 14 Minuten

Simons Benutzeravatar
Simon

Wie funktioniert Python? Slice-Notation arbeiten? Das heißt: wenn ich Code schreibe wie a[x:y:z], a[:], a[::2] usw., wie kann ich verstehen, welche Elemente im Slice landen? Bitte geben Sie gegebenenfalls Referenzen an.


Siehe Warum sind Slice- und Range-Obergrenze exklusiv? Weitere Informationen zu den Designentscheidungen hinter der Notation finden Sie hier.

Die häufigste praktische Verwendung von Slicing (und andere Möglichkeiten zur Lösung des Problems) finden Sie unter Pythonic-Methode zum Zurückgeben einer Liste jedes n-ten Elements in einer größeren Liste: Abrufen jedes n-ten Elements einer Liste. Bitte verwenden Sie diese Frage gegebenenfalls stattdessen als doppeltes Ziel.

Für genauere Antworten zu Slice-Zuordnung, siehe Wie funktioniert die Zuweisung mit Listenausschnitten? (obwohl dies auch hier angesprochen wird).

Benutzer-Avatar von Hans Nowak
Hans Nowak

Der Python-Tutorial spricht darüber (scrollen Sie ein wenig nach unten, bis Sie zum Teil über das Schneiden gelangen).

Das ASCII-Grafikdiagramm ist auch hilfreich, um sich an die Funktionsweise von Slices zu erinnern:

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

Eine Möglichkeit, sich an die Funktionsweise von Slices zu erinnern, besteht darin, sich die Indizes als Zeiger vorzustellen zwischen Zeichen, wobei der linke Rand des ersten Zeichens mit 0 nummeriert ist. Dann der rechte Rand des letzten Zeichens einer Zeichenfolge von N Zeichen haben einen Index N.

  • Dieser Vorschlag funktioniert bei positiven Schritten, nicht jedoch bei negativen Schritten. Aus dem Diagramm erwarte ich a[-4,-6,-1] sein yP aber es ist ty. Was immer funktioniert, ist, in Zeichen oder Slots zu denken und die Indizierung als halboffenes Intervall zu verwenden – rechtsoffen bei positivem Schritt, linksoffen bei negativem Schritt.

    – aguadopd

    27. Mai 2019 um 20:05 Uhr


  • Aber es gibt keine Möglichkeit, vom Ende her auf eine leere Menge zu reduzieren (z. B x[:0] Dies ist der Fall, wenn Sie von vorne beginnen. Daher müssen Sie kleine Arrays in Sonderfällen umwandeln. :/

    – Endolith

    6. Juli 2019 um 20:07 Uhr


  • @aguadopd Du hast absolut recht. Die Lösung besteht darin, die Indizes nach rechts zu verschieben, direkt unter den Zeichen zu zentrieren, und zu beachten, dass der Stopp immer ausgeschlossen ist. Eine weitere Antwort finden Sie weiter unten.

    – Javier Ruiz

    5. April 2021 um 21:32 Uhr


  • Nachtrag zu meinem Kommentar: siehe meine Antwort mit den Diagrammen unten: stackoverflow.com/a/56332104/2343869

    – aguadopd

    15. April 2021 um 1:04

  • Tatsächlich wird immer noch etwas ausgelassen, z. B. wenn ich „Apple“ eingebe.[4:-4:-1] Ich bekomme „elp“, Python übersetzt die -4 vielleicht in eine 1?

    – liyuan

    1. Januar 2018 um 16:39 Uhr


  • Beachten Sie, dass Backticks zugunsten von veraltet sind repr

    – wjandrea

    27. Januar 2019 um 1:36

  • @liyuan Der Typ wird implementiert __getitem__ Ist; Ihr Beispiel entspricht apple[slice(4, -4, -1)].

    – Chepner

    10. September 2019 um 14:26 Uhr

  • Die ersten beiden Tische sind aus purem Gold.

    – Bananeen

    20. Dezember 2021 um 4:16

David M. Perlmans Benutzeravatar
David M. Perlman

In den obigen Antworten geht es nicht um die Slice-Zuweisung. Um die Slice-Zuweisung zu verstehen, ist es hilfreich, der ASCII-Grafik ein weiteres Konzept hinzuzufügen:

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

Eine Heuristik besteht darin, für einen Abschnitt von Null bis n zu denken: „Null ist der Anfang, beginnen Sie am Anfang und nehmen Sie n Elemente in einer 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']

Eine andere Heuristik lautet: „Ersetzen Sie für jedes Segment den Anfang durch Null, wenden Sie die vorherige Heuristik an, um das Ende der Liste zu erhalten, und zählen Sie dann die erste Zahl wieder hoch, um Elemente vom Anfang abzuschneiden.“

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

Die erste Regel der Slice-Zuweisung ist die seit dem Slicing kehrt zurück eine Liste, Slice-Zuweisung erfordert eine Liste (oder eine andere iterierbare):

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

Die zweite Regel der Slice-Zuweisung, die Sie auch oben sehen können, besteht darin, dass der Teil der Liste, der durch die Slice-Indizierung zurückgegeben wird, derselbe Teil ist, der durch die Slice-Zuweisung geändert wird:

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

Die dritte Regel der Slice-Zuweisung lautet: Die zugewiesene Liste (iterierbar) muss nicht dieselbe Länge haben; Das indizierte Segment wird einfach herausgeschnitten und massenhaft durch das ersetzt, was zugewiesen wird:

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

Der schwierigste Teil, an den man sich gewöhnen muss, ist die Zuweisung zu leeren Slices. Mit Heuristik 1 und 2 ist es einfach, sich zurechtzufinden Indizierung ein leeres Slice:

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

Und wenn Sie das dann gesehen haben, macht auch die Slice-Zuweisung zum leeren Slice Sinn:

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

Beachten Sie, dass die eingefügten Elemente immer bis zum „o“ gestapelt werden, da wir die zweite Nummer des Slice (4) nicht ändern, selbst wenn wir sie dem leeren Slice zuweisen. Die Position für die Zuweisung leerer Slices ist also die logische Erweiterung der Positionen für die Zuweisungen nicht leerer Slices.

Um ein wenig zurückzukommen: Was passiert, wenn Sie mit unserer Prozession des Hochzählens des Slice-Anfangs fortfahren?

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

Wenn Sie mit dem Schneiden fertig sind, sind Sie fertig. es fängt nicht an, rückwärts zu schneiden. In Python erhalten Sie keine negativen Schritte, es sei denn, Sie fragen ausdrücklich danach, indem Sie eine negative Zahl verwenden.

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

Die Regel „Wenn du fertig bist, bist du fertig“ hat einige seltsame Konsequenzen:

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

Tatsächlich ist Python-Slicing im Vergleich zur Indizierung seltsam fehlersicher:

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

Das kann manchmal nützlich sein, kann aber auch zu etwas seltsamem Verhalten führen:

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

Abhängig von Ihrer Anwendung könnte das … oder auch nicht … das sein, was Sie sich dort erhofft haben!


Unten finden Sie den Text meiner ursprünglichen Antwort. Es war für viele Menschen nützlich, daher wollte ich es nicht löschen.

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

Dies kann auch den Unterschied zwischen Slicing und Indexierung verdeutlichen.

  • Wenn ich die ersten x Elemente einer Liste entfernen wollte, was wäre besser: l = l[6:] oder l[:] = l[6:]?

    – Alex O

    24. Juli 2022 um 18:00 Uhr

  • Der erste Weg funktioniert für eine Liste oder einen String; Die zweite Möglichkeit funktioniert nur für eine Liste, da die Slice-Zuweisung für Strings nicht zulässig ist. Abgesehen davon denke ich, dass der einzige Unterschied in der Geschwindigkeit liegt: Auf dem ersten Weg scheint es etwas schneller zu sein. Probieren Sie es selbst mit timeit.timeit() oder vorzugsweise timeit.repeat() aus. Sie sind super einfach zu bedienen und sehr lehrreich, es lohnt sich, sich daran zu gewöhnen, ständig damit zu spielen!

    – David M. Perlman

    25. Juli 2022 um 19:46 Uhr


  • Ich bin neugierig, wie zeitaufwändig die Arbeit ist r[1:1]=['blah'] ? Danke!

    – Edamame

    1. September 2022 um 17:21 Uhr

  • P[2:3] = ‘t’ funktioniert gut! Es sollte kein TypeError vorhanden sein!

    – Simo

    6. April um 11:08

Michaels Benutzeravatar
Michael

Erklären Sie die Slice-Notation von Python

Kurz gesagt, die Doppelpunkte (:) in tiefgestellter Notation (subscriptable[subscriptarg]) erstellen Sie eine Slice-Notation, die über die optionalen Argumente verfügt start, stopUnd step:

sliceable[start:stop:step]

Python-Slicing ist eine rechenschnelle Möglichkeit, methodisch auf Teile Ihrer Daten zuzugreifen. Meiner Meinung nach ist es, um auch nur ein fortgeschrittener Python-Programmierer zu sein, ein Aspekt der Sprache, mit dem man vertraut sein muss.

Wichtige Definitionen

Lassen Sie uns zunächst einige Begriffe definieren:

start: der Anfangsindex des Slice, es wird das Element an diesem Index einschließen, es sei denn, es ist dasselbe wie stoppen, ist standardmäßig 0, also der erste Index. Wenn es negativ ist, bedeutet es, anzufangen n Artikel vom Ende.

stop: Der Endindex des Slice ist der Fall nicht Fügen Sie das Element an diesem Index ein. Standardmäßig wird die Länge der in Scheiben geschnittenen Sequenz verwendet, d. h. bis einschließlich zum Ende.

step: Der Betrag, um den sich der Index erhöht, ist standardmäßig 1. Wenn er negativ ist, schneiden Sie die iterierbare Variable in umgekehrter Reihenfolge auf.

So funktioniert die Indizierung

Sie können jede dieser positiven oder negativen Zahlen eingeben. Die Bedeutung der positiven Zahlen ist einfach, aber bei negativen Zahlen zählt man, genau wie bei Indizes in Python, vom Ende an rückwärts Start Und stoppenund für die Schritt, dekrementieren Sie einfach Ihren Index. Dieses Beispiel ist aus dem Tutorial der Dokumentationaber ich habe es leicht geändert, um anzugeben, auf welches Element in einer Sequenz jeder Index verweist:

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

So funktioniert das Schneiden

Um die Slice-Notation mit einer Sequenz zu verwenden, die sie unterstützt, müssen Sie mindestens einen Doppelpunkt in die eckigen Klammern einfügen, die auf die Sequenz folgen (was eigentlich der Fall ist). das umsetzen __getitem__ Methode der Sequenz gemäß dem Python-Datenmodell.)

Die Slice-Notation funktioniert folgendermaßen:

sequence[start:stop:step]

Und denken Sie daran, dass es Standardeinstellungen für gibt Start, stoppenUnd Schrittum also auf die Standardeinstellungen zuzugreifen, lassen Sie einfach das Argument weg.

Die Slice-Notation zum Abrufen der letzten neun Elemente aus einer Liste (oder einer anderen Sequenz, die sie unterstützt, z. B. einem String) würde wie folgt aussehen:

my_list[-9:]

Wenn ich das sehe, lese ich den Teil in den Klammern als „9. vom Ende bis zum Ende“. (Eigentlich kürze ich es gedanklich als „-9, on“) ab.

Erläuterung:

Die vollständige Notation ist

my_list[-9:None:None]

und die Standardwerte zu ersetzen (eigentlich wann step ist negativ, stopDie Standardeinstellung ist -len(my_list) - 1So None denn Stopp bedeutet eigentlich nur, dass es zu dem Endschritt geht, zu dem es führt):

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

Der Doppelpunkt, :, teilt Python mit, dass Sie ihm ein Slice und keinen regulären Index geben. Aus diesem Grund lautet die idiomatische Methode zum Erstellen einer flachen Kopie von Listen in Python 2

list_copy = sequence[:]

Und das Löschen erfolgt mit:

del my_list[:]

(Python 3 erhält eine list.copy Und list.clear Methode.)

Wenn step ist negativ, die Standardwerte für start Und stop ändern

Standardmäßig, wenn die step Argument ist leer (oder None), es ist zugeordnet +1.

Sie können jedoch eine negative Ganzzahl übergeben, und die Liste (oder die meisten anderen Standard-Sliceables) wird vom Ende bis zum Anfang in Slices unterteilt.

Somit ändert ein negativer Slice die Standardeinstellungen für start Und stop!

Bestätige dies in der Quelle

Ich ermutige Benutzer gerne, sowohl die Quelle als auch die Dokumentation zu lesen. Der Quellcode für Slice-Objekte und diese Logik finden Sie hier. Zuerst stellen wir fest, ob step ist negativ:

step_is_negative = step_sign < 0;

Wenn ja, ist die Untergrenze -1 Das heißt, wir schneiden den gesamten Weg bis zum Anfang und einschließlich, und die Obergrenze ist die Länge minus 1, was bedeutet, dass wir am Ende beginnen. (Beachten Sie, dass die Semantik davon -1 Ist anders von einem -1 dass Benutzer Indizes in Python übergeben können, die das letzte Element angeben.)

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

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

Ansonsten step ist positiv, und die untere Grenze ist Null und die obere Grenze (die wir erreichen, aber nicht einbeziehen) die Länge der segmentierten Liste.

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

Dann müssen wir möglicherweise die Standardeinstellungen anwenden start Und stop– die Standardeinstellung für start wird als Obergrenze berechnet, wenn step ist negativ:

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

Und stopdie untere Grenze:

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

Geben Sie Ihren Slices einen aussagekräftigen Namen!

Möglicherweise ist es sinnvoll, das Bilden des Schnitts von der Weitergabe an den Schnitt zu trennen list.__getitem__ Methode (Das ist es, was die eckigen Klammern bewirken). Selbst wenn Sie kein Neuling darin sind, bleibt Ihr Code dadurch besser lesbar, sodass andere, die Ihren Code möglicherweise lesen müssen, leichter verstehen können, was Sie tun.

Sie können einer Variablen jedoch nicht einfach einige durch Doppelpunkte getrennte Ganzzahlen zuweisen. Sie müssen das Slice-Objekt verwenden:

last_nine_slice = slice(-9, None)

Das zweite Argument, Noneist erforderlich, damit das erste Argument als interpretiert wird start Streit sonst wäre es das stop Streit.

Anschließend können Sie das Slice-Objekt an Ihre Sequenz übergeben:

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

Es ist interessant, dass Bereiche auch Slices benötigen:

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

Überlegungen zum Speicher:

Da Slices von Python-Listen neue Objekte im Speicher erstellen, ist eine weitere wichtige Funktion zu beachten itertools.islice. Normalerweise möchten Sie ein Slice durchlaufen und es nicht nur statisch im Speicher erstellen lassen. islice ist dafür perfekt. Eine Einschränkung: Negative Argumente werden nicht unterstützt start, stopoder stepWenn dies also ein Problem darstellt, müssen Sie möglicherweise Indizes berechnen oder die Iteration im Voraus umkehren.

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

und nun:

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

Die Tatsache, dass Listenabschnitte eine Kopie erstellen, ist ein Merkmal der Listen selbst. Wenn Sie erweiterte Objekte wie einen Pandas DataFrame aufteilen, wird möglicherweise eine Ansicht des Originals und keine Kopie zurückgegeben.

  • Wenn ich die ersten x Elemente einer Liste entfernen wollte, was wäre besser: l = l[6:] oder l[:] = l[6:]?

    – Alex O

    24. Juli 2022 um 18:00 Uhr

  • Der erste Weg funktioniert für eine Liste oder einen String; Die zweite Möglichkeit funktioniert nur für eine Liste, da die Slice-Zuweisung für Strings nicht zulässig ist. Abgesehen davon denke ich, dass der einzige Unterschied in der Geschwindigkeit liegt: Auf dem ersten Weg scheint es etwas schneller zu sein. Probieren Sie es selbst mit timeit.timeit() oder vorzugsweise timeit.repeat() aus. Sie sind super einfach zu bedienen und sehr lehrreich, es lohnt sich, sich daran zu gewöhnen, ständig damit zu spielen!

    – David M. Perlman

    25. Juli 2022 um 19:46 Uhr


  • Ich bin neugierig, wie zeitaufwändig die Arbeit ist r[1:1]=['blah'] ? Danke!

    – Edamame

    1. September 2022 um 17:21 Uhr

  • P[2:3] = ‘t’ funktioniert gut! Es sollte kein TypeError vorhanden sein!

    – Simo

    6. April um 11:08

Danas Benutzeravatar
Dana

Und ein paar Dinge, die mir nicht sofort klar waren, als ich die Slicing-Syntax zum ersten Mal sah:

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

Einfache Möglichkeit, Sequenzen umzukehren!

Und wenn Sie aus irgendeinem Grund jedes zweite Element in umgekehrter Reihenfolge haben möchten:

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

1450120cookie-checkSo funktioniert das Slicing in Python

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy