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.
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
, stop
Und 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, stop
Die Standardeinstellung ist -len(my_list) - 1
So 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 stop
die 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, None
ist 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
, stop
oder step
Wenn 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.