Warum erlaubt Python Slice-Indizes außerhalb des Bereichs für Sequenzen?

76

Also bin ich gerade auf eine seltsame Python-Funktion gestoßen und wollte etwas Klarheit darüber.

Die folgende Array-Manipulation ist etwas sinnvoll:

p = [1,2,3]
p[3:] = [4] 
p = [1,2,3,4]

Ich stelle mir vor, es wird eigentlich nur dieser Wert an das Ende angehängt, richtig?
Warum kann ich das jedoch tun?

p[20:22] = [5,6]
p = [1,2,3,4,5,6]

Und noch mehr:

p[20:100] = [7,8]
p = [1,2,3,4,5,6,7,8]

Dies scheint nur eine falsche Logik zu sein. Es scheint, dass dies einen Fehler auslösen sollte!

Irgendeine Erklärung?
-Ist es nur eine seltsame Sache, die Python macht?
- Gibt es einen Zweck?
- Oder denke ich falsch darüber nach?

Akaisteph7
quelle
2
In anderen Sprachen schreibe ich immer solche Dinge überall: if i > sequence.length(): return sequence.slice(0, sequence.length()) else sequence.slice(0, n)Dies ist genau das gleiche wie bei der Verwendung sequence[:n]in Python. Es erspart Ihnen eine if-Anweisung und zwei Aufrufe von length.
Bakuriu
4
Übrigens. Sie können Slices als "Sets" betrachten. Dies p[20:22]gilt auch für eine Folge aller Elemente mit Indizes zwischen 20 und 22. Die leere Menge ist eine gültige Menge. Das ist etwas anderes als zu sagen, p[20]was die Existenz eines Elements mit Index 20 behauptet. Daher spiegelt der Unterschied in der Bereichsprüfung zwischen dem Nachschlagen eines Elements und einem Slice die zwei unterschiedlichen Bedeutungen wider.
Bakuriu
2
Ich denke, dies ist eine umfassendere Frage darüber, warum das Hinzufügen von Sequenzen in Schichten von Sequenzen unterschiedlicher Länge in Python zulässig ist und welche Vorteile dies hat. Die andere Frage befasst sich überhaupt nicht mit dem Zuordnungsteil dieser Frage. Es geht nur um das Schneiden.
Akaisteph7

Antworten:

79

Ein Teil der Frage zu Indizes außerhalb des Bereichs

Die Slice-Logik schneidet die Indizes automatisch auf die Länge der Sequenz.

Das Ermöglichen, dass sich Slice-Indizes über Endpunkte hinaus erstrecken, wurde der Einfachheit halber durchgeführt. Es wäre mühsam, jeden Ausdruck im Bereich zu überprüfen und dann die Grenzwerte manuell anzupassen, damit Python dies für Sie erledigt.

Stellen Sie sich den Anwendungsfall vor, dass nicht mehr als die ersten 50 Zeichen einer Textnachricht angezeigt werden sollen.

Der einfache Weg (was Python jetzt macht):

preview = msg[:50]

Oder auf die harte Tour (Limit-Checks selbst durchführen):

n = len(msg)
preview = msg[:50] if n > 50 else msg

Die manuelle Implementierung dieser Logik zur Anpassung der Endpunkte wäre leicht zu vergessen, wäre leicht zu verwechseln (Aktualisierung der 50 an zwei Stellen), wäre wortreich und langsam. Python verschiebt diese Logik in seine Interna, wo sie kurz, automatisch, schnell und korrekt ist. Dies ist einer der Gründe, warum ich Python liebe :-)

Ein Teil der Frage bezüglich der Nichtübereinstimmung der Zuordnungslänge mit der Eingabelänge

Das OP wollte auch die Gründe für das Zulassen von Zuweisungen kennen, z. B. p[20:100] = [7,8]wenn das Zuweisungsziel eine andere Länge (80) als die Länge der Ersatzdaten (2) hat.

Es ist am einfachsten, die Motivation durch eine Analogie mit Strings zu erkennen. Betrachten Sie , "five little monkeys".replace("little", "humongous"). Beachten Sie, dass das Ziel "klein" nur sechs Buchstaben und "humongous" neun hat. Wir können dasselbe mit Listen machen:

>>> s = list("five little monkeys")
>>> i = s.index('l')
>>> n = len('little')
>>> s[i : i+n ] = list("humongous")
>>> ''.join(s)
'five humongous monkeys'

Dies alles hängt von der Bequemlichkeit ab.

Vor der Einführung der Methoden copy () und clear () waren dies beliebte Redewendungen:

s[:] = []           # clear a list
t = u[:]            # copy a list

Selbst jetzt verwenden wir dies, um Listen beim Filtern zu aktualisieren:

s[:] = [x for x in s if not math.isnan(x)]   # filter-out NaN values

Ich hoffe, diese praktischen Beispiele geben einen guten Überblick darüber, warum das Schneiden so funktioniert.

Raymond Hettinger
quelle
2
"Sogar jetzt verwenden wir dies, um Listen beim Filtern zu aktualisieren [Beispiel mit s[:]] " - Können Sie erläutern, warum Sie s[:] =dort anstatt nur verwenden würden s =? Ich habe noch nie jemanden s[:] =im Kontext einer Zeile gesehen, wie Sie sie dort geschrieben haben. Gute Antwort sonst!
Quuxplusone
10
@Quuxplusone: Die Slice-Zuweisung mutiert die Liste, auf die bereits verwiesen wird s. Verwenden von s = Neubindungen s , um auf eine neue Liste zu verweisen. Wenn die Liste über mehrere Namen erreichbar ist und Sie möchten, dass die Mutation für alle Namen sichtbar ist, möchten Sie die Slice-Zuweisung. Wenn seine Neuzuweisung global wäre, swäre eine globalDeklaration erforderlich , aber die Slice-Zuweisung hätte auch ohne die globalAnweisung einen ähnlichen Effekt .
Daniel Pryden
24

Die Dokumentation hat Ihre Antwort:

s[i:j]: Scheibe svon ibis j(Anmerkung (4))

(4) Die Schicht svon ibis jist definiert als die Folge von Elementen mit einem Index, kso dass i <= k < j. Wenn ioder jgrößer als ist len(s), verwenden Sielen(s) . Wenn iweggelassen wird oder Noneverwenden 0. Wenn j weggelassen wird oder Noneverwenden len(s). Wenn igrößer oder gleich ist, jist das Slice leer.

Die Dokumentation vonIndexError bestätigt dieses Verhalten:

Ausnahme IndexError

Wird ausgelöst, wenn ein Sequenzindex außerhalb des Bereichs liegt. ( Slice-Indizes werden stillschweigend abgeschnitten, um in den zulässigen Bereich zu fallen. Wenn ein Index keine Ganzzahl ist, TypeErrorwird er erhöht.)

Im Wesentlichen werden Dinge wie p[20:100]auf reduziert p[len(p):len(p]. p[len(p):len(p]ist ein leeres Segment am Ende der Liste. Wenn Sie ihm eine Liste zuweisen, wird das Ende der Liste so geändert, dass es diese Liste enthält. Somit funktioniert es wie das Anhängen / Erweitern der ursprünglichen Liste.

Dieses Verhalten ist dasselbe wie das, was passiert, wenn Sie einem leeren Slice an einer beliebigen Stelle in der ursprünglichen Liste eine Liste zuweisen . Zum Beispiel:

In [1]: p = [1, 2, 3, 4]

In [2]: p[2:2] = [42, 42, 42]

In [3]: p
Out[3]: [1, 2, 42, 42, 42, 3, 4]
iz_
quelle
3
Ich glaube nicht, dass OP fragt, wie das Schneiden funktioniert, er fragt nach den Gründen für die Wahl des Designs.
Primusa
2
@Primusa - Ich glaube, sie fragen beide . Dies erklärt das Wie , was gut zu wissen ist, weil es erklärt, warum das Verhalten nicht gebrochen ist. Das Warum ist wahrscheinlich irgendwo in den Tiefen einer der Mailinglisten vergraben.
GDDC
Gute Antwort, aber das erklärt nicht, warum die neuen Nummern an das Ende der Liste angehängt werden.
Atirag
1
@ Atirag Ich habe der Vollständigkeit halber einen kleinen Klappentext hinzugefügt.
iz_
1
@Atirag Die Indizierung unterscheidet sich stark vom Schneiden. Indizierung bezieht sich immer auf Werte.
iz_