Python-Grundlagen

Programmiergrundlagen
Python
R
Datentypen, Kontrollfluss, Listen und Dictionaries als Arbeitsbasis.

Kernideen

  • Ein Name ist ein Verweis auf ein Objekt, kein Behälter. Zwei Namen können auf dasselbe Objekt zeigen.
  • Vier Container decken den Alltag ab: Liste, Tupel, Dictionary, Set. Die Wahl hängt daran, ob Reihenfolge, Veränderbarkeit oder Schlüssel gebraucht werden.
  • Python zählt ab null und schliesst beim Slicing die obere Grenze aus. Ein negativer Index heisst “von hinten”, in R heisst er “weglassen”.
  • Blöcke entstehen durch Einrückung, nicht durch Klammern.
  • Eine Liste rechnet nicht elementweise. Dafür gibt es Comprehensions oder NumPy.
  • Gleitkommazahlen vergleicht man nie mit ==, sondern mit einer Toleranz.

Erklärung

Vorwissen: keines. Diese Seite ist der Einstieg in Python. Wer von R herkommt, findet in jedem Beispiel den Vergleich; wer hier anfängt, kann den R-Reiter überspringen. Weiter geht es mit Funktionen und Module in Python.

Ein Name ist kein Behälter

In vielen Sprachen deklariert man eine Variable mit einem Typ, und dieser Typ gilt. Python macht es anders: Ein Name ist nur ein Verweis auf ein Objekt, und derselbe Name kann nacheinander auf Objekte ganz verschiedener Art zeigen.

x = 42          # x zeigt auf eine ganze Zahl
x = "Text"      # jetzt auf eine Zeichenkette, ohne Fehler

Das macht den Einstieg schnell und verschiebt Fehler von der Übersetzungs- in die Laufzeit. Ein Tippfehler in einem Namen fällt erst auf, wenn die Zeile ausgeführt wird. Die Konsequenz für die Praxis: früh und oft ausführen, statt lange zu schreiben und dann zu starten.

Die zweite Folge ist die wichtigere. Schreibt man b = a, entsteht kein neues Objekt, sondern ein zweiter Name für dasselbe. Ändert man das Objekt über b, sieht man die Änderung auch über a. R verhält sich hier anders (copy on modify), und genau das macht Beispiel 1 zur ersten Stolperstelle beim Sprachwechsel.

Die vier Container

Typ Schreibweise geordnet veränderlich Zugriff über wofür
Liste [1, 2, 3] ja ja Position der Normalfall
Tupel (1, 2, 3) ja nein Position feste Wertegruppen, Rückgabe mehrerer Werte
Dictionary {"a": 1} ja (Einfügung) ja Schlüssel benannte Daten
Set {1, 2, 3} nein ja nur Mitgliedschaft Eindeutigkeit, Mengenoperationen

Ein Dictionary-Schlüssel muss unveränderlich sein. Ein Tupel taugt deshalb als Schlüssel, eine Liste nicht. Das ist der eigentliche Grund, warum es beide Typen gibt.

Das Gegenstück in R

Python R Unterschied, der beisst
Liste [3, 1, 4] atomarer Vektor c(3, 1, 4) der R-Vektor rechnet elementweise, die Liste nicht
Liste mit gemischten Typen list(...)
Dictionary benannte Liste list(a = 1) fehlender Schlüssel: Python KeyError, R NULL
Set unique(), union(), intersect() R hat keinen eigenen Mengentyp
werte[0] werte[1] Zählung ab null gegen ab eins
werte[-1] werte[length(werte)] werte[-1] heisst in R “ohne das erste”

Kontrollfluss und Einrückung

Python bildet Blöcke mit Einzug. Das erzwingt lesbaren Code und bestraft gemischte Tabulatoren und Leerzeichen sofort mit einem IndentationError.

for w in werte:
    if w > 40:
        art = "hoch"
    elif w > 10:
        art = "mittel"
    else:
        art = "tief"

Eine Schleife, die nichts tut als eine Liste zu füllen, schreibt man als Comprehension: [w ** 2 for w in werte if w > 10]. Das ist kein Stilmittel, sondern die idiomatische Form. R braucht dafür keine Schleife, weil dort jede Rechnung auf dem ganzen Vektor läuft.

Wo man arbeitet

Umgebung wofür sie taugt Grenze
Konsole einzelne Ausdrücke ausprobieren nichts bleibt erhalten
Jupyter Analyse mit Text, Code und Ausgabe in einem Dokument Ausführungsreihenfolge kann von der Lesereihenfolge abweichen
Spyder Skripte mit Variablenübersicht, nahe an RStudio weniger für grosse Projekte
PyCharm, VS Code mehrere Dateien, Tests, Refactoring schwergewichtig für eine kurze Auswertung

Die Falle bei Notebooks ist real. Wer Zellen in beliebiger Reihenfolge ausführt, erzeugt einen Zustand, der sich beim Durchlauf von oben nicht reproduzieren lässt. Die Probe: Kernel neu starten und alles von oben ausführen. Läuft es dann nicht durch, war das Ergebnis nie reproduzierbar. Mehr dazu unter Reproduzierbarkeit.

Kurz nachgeschlagen

Aufgabe R Python
Liste filtern und umformen x[x %% 2 == 0]^2 [v ** 2 for v in x if v % 2 == 0]
über Schlüssel und Werte laufen for (n in names(p)) p[[n]] for n, w in p.items():
Text mit Werten sprintf("%s: %.2f", n, w) f"{n}: {w:.2f}"
Häufigkeiten table(x) Counter(x)
zwei Listen paarweise for (i in seq_along(a)) for u, v in zip(a, b):
Wert mit Ersatz holen if (is.null(p$d)) 0 else p$d p.get("d", 0)
Typ abfragen class(x) type(x)

Beispiele

Frage und Datenlage

Eine kleine Liste wird einem zweiten Namen zugewiesen und über diesen verändert. Was passiert mit dem Original, in Python und in R? Und reicht copy(), wenn die Liste selbst Listen enthält?

Rechnung

a <- c(1, 2, 3)
b <- a
b[4] <- 4
list(a = a, b = b)
$a
[1] 1 2 3

$b
[1] 1 2 3 4
# Auch verschachtelt kopiert R beim Aendern
tief <- list(c(1, 2), c(3, 4))
flach <- tief
flach[[1]] <- c(flach[[1]], 99)
tief[[1]]
[1] 1 2
a = [1, 2, 3]
b = a
b.append(4)
print({"a": a, "b": b, "a_is_b": a is b})
{'a': [1, 2, 3, 4], 'b': [1, 2, 3, 4], 'a_is_b': True}
c = a.copy()
c.append(5)
print({"a": a, "c": c, "a_is_c": a is c})
{'a': [1, 2, 3, 4], 'c': [1, 2, 3, 4, 5], 'a_is_c': False}
# Neu zuweisen ist etwas anderes als veraendern
x = [1, 2, 3]
y = x
y = y + [4]
print({"x": x, "y": y, "x_is_y": x is y})
{'x': [1, 2, 3], 'y': [1, 2, 3, 4], 'x_is_y': False}
# copy() ist flach: innere Listen bleiben geteilt
tief = [[1, 2], [3, 4]]
flach = tief.copy()
echt = copy.deepcopy(tief)
flach[0].append(99)
print({"tief": tief, "echt": echt})
{'tief': [[1, 2, 99], [3, 4]], 'echt': [[1, 2], [3, 4]]}

Output Zeile für Zeile

Zeile Python R Erklärung
a nach Änderung über b [1, 2, 3, 4] 1 2 3 In Python sind a und b dasselbe Objekt (a_is_b ist True). R kopiert in dem Moment, in dem b verändert wird.
a nach c = a.copy() [1, 2, 3, 4] copy() erzeugt ein neues Objekt, a_is_c ist False, das Anhängen an c lässt a in Ruhe.
x nach y = y + [4] [1, 2, 3] + baut eine neue Liste und bindet y neu. append dagegen verändert das Objekt, auf das beide zeigen.
tief nach flach[0].append(99) [[1, 2, 99], [3, 4]] 1 2 copy() kopiert nur die äussere Liste. Die inneren Listen sind weiterhin geteilt. Erst copy.deepcopy trennt vollständig, echt bleibt [[1, 2], [3, 4]].

Merkregel: Methoden wie append, extend, sort und Zuweisungen an liste[i] verändern das Objekt, und alle Namen sehen es. Operatoren wie + und Funktionen wie sorted() liefern ein neues Objekt. Wer unsicher ist, prüft mit a is b.

Interpretation und Ergebnissatz

Die Falle ist nicht die Zuweisung, sondern die Kombination aus Zuweisung und veränderndem Methodenaufruf. In R kann sie nicht auftreten, weil dort jede Änderung eine Kopie erzeugt, sobald ein zweiter Name auf das Objekt zeigt. Wer Code von R nach Python überträgt, muss deshalb an genau diesen Stellen eine Kopie einbauen.

Nach b = a und b.append(4) enthält auch a die 4, weil beide Namen auf dieselbe Liste zeigen; in R bleibt a bei 1 2 3. copy() trennt nur die äussere Ebene: Bei verschachtelten Listen braucht es copy.deepcopy().

Frage und Datenlage

Fünf Messwerte 12.1, 14.3, 11.8, 15.0, 13.6. Wie holt man den ersten, den letzten, einen Ausschnitt, und was passiert bei einem Index jenseits des Endes?

Rechnung

werte <- c(12.1, 14.3, 11.8, 15.0, 13.6)
werte[1]
[1] 12.1
werte[2:3]
[1] 14.3 11.8
werte[-1]              # OHNE das erste, nicht das letzte
[1] 14.3 11.8 15.0 13.6
werte[length(werte)]
[1] 13.6
rev(werte)
[1] 13.6 15.0 11.8 14.3 12.1
werte[10]              # kein Fehler, sondern NA
[1] NA
werte = [12.1, 14.3, 11.8, 15.0, 13.6]
print(werte[0])
12.1
print(werte[1:3])
[14.3, 11.8]
print(werte[-1])       # das letzte
13.6
print(werte[1:])       # ohne das erste
[14.3, 11.8, 15.0, 13.6]
print(werte[::-1])
[13.6, 15.0, 11.8, 14.3, 12.1]
try:
    werte[10]
except IndexError as fehler:
    print(type(fehler).__name__, fehler)
IndexError list index out of range
print(werte[3:10])     # Slicing ueber das Ende hinaus ist erlaubt
[15.0, 13.6]

Output Zeile für Zeile

Absicht Python Ergebnis R Ergebnis
erstes Element werte[0] 12.1 werte[1] 12.1
zweites und drittes werte[1:3] [14.3, 11.8] werte[2:3] 14.3 11.8
letztes werte[-1] 13.6 werte[length(werte)] 13.6
alle ausser dem ersten werte[1:] vier Werte ab 14.3 werte[-1] vier Werte ab 14.3
umgekehrt werte[::-1] [13.6, 15.0, 11.8, 14.3, 12.1] rev(werte) dieselben Werte
Index 10 werte[10] IndexError werte[10] NA
Ausschnitt über das Ende werte[3:10] [15.0, 13.6]

Drei Unterschiede stecken in dieser Tabelle:

  1. Start bei null. werte[1] ist in Python der zweite Wert.
  2. Obere Grenze ausgeschlossen. werte[1:3] liefert die Positionen 1 und 2, also zwei Werte. Der Vorteil: Die Länge des Ausschnitts ist die Differenz der Grenzen, und werte[:k] und werte[k:] ergeben zusammen die ganze Liste ohne Überlappung.
  3. Negativer Index. In Python zählt er von hinten, in R entfernt er das Element. werte[-1] liefert in Python einen Wert, in R vier.

Interpretation und Ergebnissatz

Python bricht bei einem ungültigen Einzelindex ab, R gibt stillschweigend NA zurück und rechnet weiter. Das Python-Verhalten ist lauter und deshalb sicherer: Ein falscher Index fällt sofort auf, statt als fehlender Wert in einem Mittelwert zu verschwinden. Slicing ist dagegen tolerant und kürzt einfach ab.

Der Ausschnitt werte[1:3] enthält die zwei Werte 14.3 und 11.8, weil Python ab null zählt und die obere Grenze ausschliesst. werte[-1] ist in Python der letzte Wert 13.6, in R dagegen der Vektor ohne das erste Element.

Frage und Datenlage

In einem Prüfprotokoll stehen die Teiletypen A, B, A, C, A, B. Welche Typen kommen vor, wie oft, welcher am häufigsten? Dazu ein Lagerbestand je Typ, der um einen Eintrag ergänzt und nach einem fehlenden Typ gefragt wird.

Rechnung

teile <- c("A", "B", "A", "C", "A", "B")
unique(teile)
[1] "A" "B" "C"
table(teile)
teile
A B C 
3 2 1 
names(which.max(table(teile)))
[1] "A"
lager <- list(A = 120, B = 45)
lager$C <- 8
str(lager)
List of 3
 $ A: num 120
 $ B: num 45
 $ C: num 8
lager$D                # fehlender Schluessel: NULL, kein Fehler
NULL
teile = ["A", "B", "A", "C", "A", "B"]
print(sorted(set(teile)))
['A', 'B', 'C']
zaehlung = Counter(teile)
print(zaehlung)
Counter({'A': 3, 'B': 2, 'C': 1})
print(zaehlung.most_common(1))
[('A', 3)]
lager = {"A": 120, "B": 45}
lager["C"] = 8
print(lager)
{'A': 120, 'B': 45, 'C': 8}
print(lager.get("D", 0))
0
try:
    lager["D"]
except KeyError as fehler:
    print(type(fehler).__name__, fehler)
KeyError 'D'
# Tupel als Schluessel geht, Liste nicht
orte = {(47.37, 8.54): "Zürich"}
print(orte[(47.37, 8.54)])
Zürich
try:
    {[47.37, 8.54]: "Zürich"}
except TypeError as fehler:
    print(type(fehler).__name__)
TypeError

Output Zeile für Zeile

Frage Python R Container
welche Typen ['A', 'B', 'C'] "A" "B" "C" Set, in R unique()
wie oft Counter({'A': 3, 'B': 2, 'C': 1}) Tabelle mit 3, 2, 1 Counter ist ein Dictionary mit Zählfunktionen
häufigster [('A', 3)] "A" most_common(1) liefert eine Liste von Tupeln aus Wert und Anzahl
Lager nach Ergänzung {'A': 120, 'B': 45, 'C': 8} Liste mit drei Einträgen Dictionary, in R benannte Liste
fehlender Typ D mit get 0 get mit Ersatzwert ist der übliche Weg
fehlender Typ D direkt KeyError 'D' NULL Python bricht ab, R liefert ein leeres Objekt
Tupel als Schlüssel Zürich Tupel sind unveränderlich und damit als Schlüssel zulässig
Liste als Schlüssel TypeError Listen sind veränderlich und nicht hashbar

Warum sorted(set(...)) und nicht nur set(...): Ein Set hat keine Reihenfolge. Die Ausgabe eines Sets aus Zeichenketten kann sich zwischen zwei Programmläufen ändern. Wer eine reproduzierbare Ausgabe will, sortiert.

Interpretation und Ergebnissatz

Die Wahl des Containers ist eine Frage an die Daten: Zählt die Reihenfolge (Liste), soll sich nichts ändern (Tupel), wird über Namen zugegriffen (Dictionary), oder geht es nur um Vorkommen (Set)? Mit der richtigen Wahl werden die Rechnungen Einzeiler.

Im Protokoll kommen die drei Typen A, B und C vor, A dreimal und damit am häufigsten. Der fehlende Typ D führt bei direktem Zugriff zu einem KeyError; mit lager.get("D", 0) erhält man stattdessen den Ersatzwert 0.

Frage und Datenlage

Fünf Werte 12, 45, 7, 31, 88 sollen in die Klassen tief, mittel und hoch eingeteilt, quadriert, gefiltert und gezählt werden. Dazu die Probe, was werte * 2 tut.

Rechnung

werte <- c(12, 45, 7, 31, 88)
art <- ifelse(werte > 40, "hoch", ifelse(werte > 10, "mittel", "tief"))
data.frame(werte, art)
  werte    art
1    12 mittel
2    45   hoch
3     7   tief
4    31 mittel
5    88   hoch
werte^2
[1]  144 2025   49  961 7744
werte[werte > 30]
[1] 45 31 88
werte * 2
[1]  24  90  14  62 176
sum(werte > 30)
[1] 3
werte = [12, 45, 7, 31, 88]
for w in werte:
    if w > 40:
        art = "hoch"
    elif w > 10:
        art = "mittel"
    else:
        art = "tief"
    print(f"{w:>3} -> {art}")
 12 -> mittel
 45 -> hoch
  7 -> tief
 31 -> mittel
 88 -> hoch
print([w ** 2 for w in werte])
[144, 2025, 49, 961, 7744]
print([w for w in werte if w > 30])
[45, 31, 88]
print({w: w ** 2 for w in werte if w < 20})
{12: 144, 7: 49}
print(werte * 2)
[12, 45, 7, 31, 88, 12, 45, 7, 31, 88]
print(sum(1 for w in werte if w > 30))
3

Output Zeile für Zeile

Aufgabe Python R Ergebnis
Klassen for mit if/elif/else ifelse() verschachtelt 12 und 31 mittel, 45 und 88 hoch, 7 tief
quadrieren [w ** 2 for w in werte] werte^2 144, 2025, 49, 961, 7744
filtern [w for w in werte if w > 30] werte[werte > 30] 45, 31, 88
Dictionary bauen {w: w ** 2 for w in werte if w < 20} {12: 144, 7: 49}
mal zwei werte * 2 werte * 2 Python: die Liste zweimal hintereinander, zehn Elemente. R: 24, 90, 14, 62, 176
zählen sum(1 for w in werte if w > 30) sum(werte > 30) 3

f"{w:>3}" in der Schleife ist ein f-String: Der Ausdruck in geschweiften Klammern wird eingesetzt, :>3 richtet ihn rechtsbündig auf drei Stellen aus. Deshalb stehen die Pfeile untereinander.

In R zählt sum(werte > 30) die TRUE-Werte, weil ein logischer Vektor beim Summieren zu 0 und 1 wird. Python kann dasselbe mit sum(w > 30 for w in werte), denn auch dort ist True gleich 1.

Interpretation und Ergebnissatz

Die Zeile werte * 2 ist der klassische Stolperstein beim Wechsel von R: Auf einer Liste wiederholt der Stern die Liste, statt die Elemente zu multiplizieren. Wer elementweise rechnen will, nimmt eine Comprehension oder wechselt zu einem NumPy-Array, das sich dann wie ein R-Vektor verhält.

Drei der fünf Werte liegen über 30 (45, 31 und 88). werte * 2 verdoppelt in Python die Länge der Liste auf zehn Elemente; elementweise rechnet erst eine Comprehension oder ein NumPy-Array.

Frage und Datenlage

Drei Dinge, die in beiden Sprachen überraschen: was 7 / 2 und die ganzzahlige Division liefern, warum 0.1 + 0.2 == 0.3 falsch ist, und wie round(2.5) rundet.

Rechnung

c(7 / 2, 7 %/% 2, 7 %% 2, -7 %/% 2, -7 %% 2)
[1]  3.5  3.0  1.0 -4.0  1.0
c(class(7 / 2), class(7L %/% 2L), class(5))
[1] "numeric" "integer" "numeric"
0.1 + 0.2 == 0.3
[1] FALSE
print(0.1 + 0.2, digits = 17)
[1] 0.30000000000000004
isTRUE(all.equal(0.1 + 0.2, 0.3))
[1] TRUE
c(round(0.5), round(1.5), round(2.5), round(2.675, 2))
[1] 0.00 2.00 2.00 2.67
print(7 / 2, 7 // 2, 7 % 2, -7 // 2, -7 % 2)
3.5 3 1 -4 1
print(type(7 / 2).__name__, type(7 // 2).__name__, type(5).__name__)
float int int
print(0.1 + 0.2 == 0.3, 0.1 + 0.2)
False 0.30000000000000004
print(math.isclose(0.1 + 0.2, 0.3))
True
print(round(0.5), round(1.5), round(2.5), round(2.675, 2))
0 2 2 2.67
print(f"{2.675:.20f}")
2.67499999999999982236

Output Zeile für Zeile

Ausdruck Python R Erklärung
7 / 2 3.5 3.5 Echte Division, in beiden Sprachen eine Gleitkommazahl.
ganzzahlige Division 7 // 2 = 3 7 %/% 2 = 3 Abrunden auf die nächste ganze Zahl.
Rest 7 % 2 = 1 7 %% 2 = 1
negativ geteilt -7 // 2 = −4 -7 %/% 2 = −4 Abgerundet wird nach unten, nicht Richtung null. −3.5 wird zu −4.
Rest bei negativ -7 % 2 = 1 -7 %% 2 = 1 Der Rest hat das Vorzeichen des Teilers, damit (a // b) * b + a % b == a gilt.
Typ von 5 int numeric Eine ganze Zahl ohne Nachsatz ist in R eine Gleitkommazahl. Ganzzahlig wird sie erst mit 5L.
0.1 + 0.2 == 0.3 False FALSE 0.1 und 0.2 sind binär nicht exakt darstellbar. Die Summe ist 0.30000000000000004.
Vergleich mit Toleranz math.iscloseTrue all.equalTRUE So vergleicht man Gleitkommazahlen.
round(0.5), round(1.5), round(2.5) 0, 2, 2 0, 2, 2 Auf die gerade Zahl gerundet (Banker’s Rounding), nicht kaufmännisch. Das verhindert einen systematischen Aufwärtsfehler bei vielen Rundungen.
round(2.675, 2) 2.67 2.67 Kein Rundungsfehler: 2.675 ist intern 2.67499999999999982236, also knapp unter der Mitte.

Interpretation und Ergebnissatz

Beide Sprachen folgen demselben Gleitkommastandard, deshalb verhalten sie sich bei Rundung und Vergleich gleich. Der einzige echte Unterschied in diesem Beispiel ist der Typ: Python unterscheidet int und float schon am Literal, R nicht. Das zählt, wenn Werte als Index oder in einer Datenbank landen.

0.1 + 0.2 ergibt in beiden Sprachen 0.30000000000000004, weshalb der Vergleich mit == scheitert und eine Toleranz (math.isclose, all.equal) nötig ist. round(2.5) liefert 2, weil auf die gerade Zahl gerundet wird.

Verständnisfragen

werte = [3, 1, 4, 1, 5]. Was liefert werte[0:2]?

Zwei Elemente, nämlich [3, 1]
Richtig. Die obere Grenze ist ausgeschlossen. Der Vorteil dieser Regel ist, dass die Länge des Ausschnitts die Differenz der beiden Zahlen ist.
Drei Elemente, [3, 1, 4]
Das wäre bei eingeschlossener Grenze der Fall, wie bei werte[1:3] in R. Beim Sprachwechsel ist das die häufigste Falle.
Einen Fehler, weil die Liste bei 1 beginnt
Python beginnt bei 0.

Nach a = [1, 2], b = a und b.append(3): Was enthält a?

[1, 2, 3]
Richtig. b = a erzeugt keine Kopie, beide Namen zeigen auf dieselbe Liste, und append verändert diese Liste. Beispiel 1 zeigt den Nachweis mit a is b.
[1, 2]
So verhielte sich R, das beim Ändern kopiert. In Python braucht es dafür b = a.copy().
Einen Fehler, weil a nicht verändert wurde
Der Aufruf ist gültig.

Wann ist ein Tupel der Liste vorzuziehen?

Wenn die Werte nur gelesen werden sollen
Nur lesen kann man aus einer Liste ebenso gut. Der Unterschied ist, ob sich der Inhalt ändern lässt.
Wenn die Gruppe als Dictionary-Schlüssel dienen soll
Richtig. Schlüssel müssen unveränderlich sein, weshalb ein Koordinatenpaar als Tupel funktioniert und als Liste einen TypeError auslöst.
Wenn verschiedene Typen gemischt werden
Das können beide.

Was ergibt [0] * 3?

[0, 0, 0]
Richtig. Bei Listen wiederholt der Stern die Liste. Wer die Elemente multiplizieren will, braucht eine Comprehension oder NumPy.
[0], weil null mal drei null ist
Das wäre elementweise Multiplikation. Genau diese Verwechslung ist gemeint.
Einen Fehler
Die Operation ist gültig und wird oft genutzt, um eine Liste vorzubelegen.

Ein Skript prüft if summe == 0.3: nach summe = 0.1 + 0.2. Was passiert?

Der Zweig wird ausgeführt
Die Summe ist 0.30000000000000004, nicht 0.3.
Der Zweig wird übersprungen, richtig wäre math.isclose(summe, 0.3)
Richtig. Gleitkommazahlen werden mit einer Toleranz verglichen. In R entspricht das isTRUE(all.equal(summe, 0.3)).
Python rundet automatisch und der Vergleich gelingt
Weder Python noch R runden beim Vergleich.

Ein Code-Stück aus R lautet werte[-1] und wird wörtlich nach Python übertragen. Was ändert sich?

Aus “alle ausser dem ersten” wird “nur das letzte”
Richtig. Der negative Index hat in den beiden Sprachen verschiedene Bedeutungen. Das Python-Gegenstück zum R-Ausdruck ist werte[1:].
Nichts, beide entfernen das erste Element
Nur R entfernt.
Python meldet einen Fehler
Negative Indizes sind in Python gültig.

Verlinkte Ressourcen