cat posts/f-strings-bis-zum-anschlag.md
f-Strings bis zum Anschlag
Ausrichtung, Präzision, Tausendertrenner, Debug-Syntax und dynamische Formatangaben – so nutzt Du die Format-Mini-Sprache der f-Strings vollständig aus.
In Teil 2 der Python-Serie haben wir f-Strings als bequeme Möglichkeit kennengelernt, Werte in Strings einzubauen:
name = "Karl"
hp = 75
print(f"{name} hat noch {hp} Lebenspunkte.")
Das ist allerdings nur die einfachste Form. f-Strings können Zahlen runden, Spalten ausrichten, Vorzeichen erzwingen, Datumswerte formatieren und beim Debugging gleichzeitig einen Ausdruck und dessen Ergebnis anzeigen.
Der größte Teil dieser Möglichkeiten steckt hinter dem Doppelpunkt innerhalb eines Replacement Fields:
f"{wert:formatangabe}"
Dort beginnt die Format Specification Mini-Language, kurz Format-Mini-Sprache.
Die Grundform eines f-Strings
Ein f-String beginnt mit einem f direkt vor dem öffnenden Anführungszeichen:
name = "Karl"
text = f"Willkommen, {name}!"
Alles zwischen den geschweiften Klammern wird als Python-Ausdruck ausgewertet:
hp = 75
max_hp = 100
print(f"Fehlende HP: {max_hp - hp}")
Die Ausgabe lautet:
Fehlende HP: 25
Du bist nicht auf einfache Variablennamen beschränkt. Innerhalb eines Replacement Fields kannst Du Berechnungen durchführen, Funktionen und Methoden aufrufen oder auf Elemente von Listen und Dictionaries zugreifen:
spieler = {
"name": "Karl",
"hp": 75,
}
print(f"Name: {spieler['name'].upper()}")
print(f"Doppelte HP: {spieler['hp'] * 2}")
print(f"Status: {'lebendig' if spieler['hp'] > 0 else 'besiegt'}")
Ausgabe:
Name: KARL
Doppelte HP: 150
Status: lebendig
Nur weil fast jeder Ausdruck erlaubt ist, muss man ihn allerdings nicht in einen f-String quetschen. Sobald Du beim Lesen erst Klammern zählen oder eine längere Berechnung nachvollziehen musst, ist eine Zwischenvariable meist klarer.
Die Format-Mini-Sprache
Nach dem Ausdruck kannst Du mit einem Doppelpunkt eine Formatangabe ergänzen:
f"{wert:formatangabe}"
Ein paar einfache Beispiele:
>>> f"{3.14159:.2f}"
'3.14'
>>> f"{42:5}"
' 42'
>>> f"{'links':<10}|"
'links |'
.2f bedeutet hier: Festkommadarstellung mit zwei Nachkommastellen.
Die 5 reserviert mindestens fünf Zeichen für die Zahl. Da Zahlen
standardmäßig rechtsbündig ausgerichtet werden, entstehen vor der 42 drei
Leerzeichen.
Die Standard-Mini-Sprache lässt sich für Python 3.14 vereinfacht so zusammenfassen:
[[Füllzeichen]Ausrichtung][Vorzeichen][z][#][0]
[Breite][Gruppierung][.Präzision[Gruppierung]][Typ]
Nicht jede Option funktioniert mit jedem Datentyp. z ergibt beispielsweise nur
bei Fließkommadarstellungen Sinn, während b als Typ für Ganzzahlen gedacht ist.
Außerdem dürfen Klassen selbst festlegen, wie sie eine Formatangabe interpretieren. Dazu kommen wir später noch.
Die komplette Reihenfolge muss man nicht auswendig lernen. In der Praxis kombiniert man meistens nur zwei oder drei dieser Bestandteile.
Ausrichtung und Breite
Mit einer Zahl legst Du die Mindestbreite des Feldes fest:
>>> f"{42:5}"
' 42'
>>> f"{'Karl':10}"
'Karl '
Zahlen sind standardmäßig rechtsbündig, Strings linksbündig.
Mit einem Alignment-Zeichen kannst Du die Ausrichtung ausdrücklich festlegen:
| Zeichen | Bedeutung |
|---|---|
< |
linksbündig |
> |
rechtsbündig |
^ |
zentriert |
= |
Padding nach dem Vorzeichen, nur für numerische Typen |
Beispiele:
>>> f"{'links':<10}|"
'links |'
>>> f"{'rechts':>10}|"
' rechts|'
>>> f"{'mitte':^10}|"
' mitte |'
Die | gehören nicht zur Formatierung. Sie machen nur sichtbar, wo das
reservierte Feld endet.
Eigenes Füllzeichen verwenden
Direkt vor dem Alignment-Zeichen kannst Du ein eigenes Füllzeichen angeben:
>>> f"{'Dungeon':-^20}"
'------Dungeon-------'
>>> f"{'Warnung':!<20}"
'Warnung!!!!!!!!!!!!!'
>>> f"{42:.>8}"
'......42'
Die allgemeine Form lautet:
Füllzeichen + Ausrichtung + Breite
Bei
f"{'Dungeon':-^20}"
bedeutet das:
-füllt die freien Stellen mit Bindestrichen,^zentriert den Wert,20reserviert mindestens 20 Zeichen.
Das Füllzeichen besteht aus genau einem Zeichen.
Eine Besonderheit: { und } kannst Du innerhalb eines f-Strings nicht direkt
als Füllzeichen verwenden, weil sie dort bereits die Replacement Fields
begrenzen.
Einfache Tabellen ausrichten
Feste Breiten eignen sich gut für kleine Texttabellen:
spieler = [
("Karl", 100, 50),
("Goblin", 30, 4),
("Drache", 999, 12500),
]
print(f"{'Name':<10} {'HP':>5} {'Gold':>8}")
print("-" * 25)
for name, hp, gold in spieler:
print(f"{name:<10} {hp:>5} {gold:>8}")
Ausgabe:
Name HP Gold
-------------------------
Karl 100 50
Goblin 30 4
Drache 999 12500
Die drei Felder
{name:<10}
{hp:>5}
{gold:>8}
reservieren jeweils eine feste Mindestbreite und richten den Inhalt darin aus.
Für eine kleine Terminalausgabe reicht das oft völlig aus. Bei Emojis, manchen asiatischen Schriftzeichen oder kombinierten Unicode-Zeichen wird es allerdings komplizierter: Die Anzahl der Zeichen in einem Python-String muss nicht mit der sichtbaren Breite im Terminal übereinstimmen.
Zahlen mit Nullen auffüllen
Ganzzahlen lassen sich mit führenden Nullen auf eine Mindestbreite bringen:
>>> f"{7:03}"
'007'
>>> f"{42:05}"
'00042'
Die Schreibweise besteht im einfachsten Fall aus:
0 + Breite
Das ist beispielsweise für fortlaufende Nummern praktisch:
for nummer in range(1, 4):
print(f"Raum-{nummer:03}")
Ausgabe:
Raum-001
Raum-002
Raum-003
Die explizite Variante mit Füllzeichen und Alignment funktioniert ebenfalls:
>>> f"{7:0>3}"
'007'
Bei positiven Ganzzahlen sehen 03 und 0>3 gleich aus. Bei Zahlen mit
Vorzeichen gibt es aber einen wichtigen Unterschied:
>>> f"{-42:05}"
'-0042'
Die spezielle numerische Nullauffüllung behandelt das Vorzeichen separat. Die Nullen landen zwischen Minuszeichen und Ziffern.
Das entspricht intern dem Alignment = mit 0 als Füllzeichen.
Nachkommastellen festlegen
Für float-Werte wird häufig der Typ f verwendet:
>>> f"{3.14159:.2f}"
'3.14'
>>> f"{3.14159:.4f}"
'3.1416'
Die Zahl hinter dem Punkt bestimmt bei f die Anzahl der Nachkommastellen.
Python rundet die Darstellung entsprechend:
>>> f"{1.999:.2f}"
'2.00'
Der gespeicherte Wert selbst wird dabei nicht verändert:
wert = 3.14159
print(f"{wert:.2f}")
print(wert)
Ausgabe:
3.14
3.14159
Die Formatierung betrifft nur den erzeugten String.
Breite und Präzision kombinieren
Mehrere Angaben lassen sich miteinander verbinden:
>>> f"{3.14159:8.2f}"
' 3.14'
>>> f"{3.14159:08.2f}"
'00003.14'
08.2f bedeutet:
- mindestens acht Zeichen breit,
- mit Nullen auffüllen,
- zwei Nachkommastellen,
- Darstellung als Festkommazahl.
Bei mehreren Zahlen lassen sich damit Dezimalstellen sauber ausrichten:
werte = [3.5, 12.75, 123.456]
for wert in werte:
print(f"{wert:8.2f}")
Ausgabe:
3.50
12.75
123.46
Strings mit Präzision kürzen
Die Präzision hat bei Strings eine andere Bedeutung. Sie legt dort die maximale Anzahl der verwendeten Zeichen fest:
>>> f"{'Dungeonmeister':.7}"
'Dungeon'
Breite und maximale Länge lassen sich kombinieren:
>>> f"{'Dungeonmeister':10.7}"
'Dungeon '
Das Feld ist mindestens zehn Zeichen breit, vom eigentlichen String werden aber nur sieben Zeichen verwendet.
Das ist ein wichtiger Unterschied:
f"{text:10}"
kürzt einen langen String nicht. Die 10 ist nur eine Mindestbreite.
f"{text:.10}"
beschränkt dagegen die ausgegebene Länge auf zehn Zeichen.
Prozentwerte formatieren
Der Formattyp % multipliziert einen Wert mit 100 und hängt ein
Prozentzeichen an:
>>> f"{0.1337:%}"
'13.370000%'
>>> f"{0.1337:.1%}"
'13.4%'
>>> f"{0.5:.0%}"
'50%'
Die Präzision bestimmt die Zahl der Nachkommastellen.
Ein Wert von 0.25 ergibt bei dieser Formatierung also 25%, nicht
0.25%.
fortschritt = 0.725
print(f"Fortschritt: {fortschritt:.1%}")
Ausgabe:
Fortschritt: 72.5%
Ziffern gruppieren
Mit einem Komma kannst Du große Dezimalzahlen in Dreiergruppen aufteilen:
>>> f"{1234567:,}"
'1,234,567'
Das lässt sich mit Nachkommastellen kombinieren:
>>> f"{1234567.891:,.2f}"
'1,234,567.89'
Statt eines Kommas ist auch ein Unterstrich möglich:
>>> f"{1234567:_}"
'1_234_567'
Dabei handelt es sich um feste Zeichen. :, erzeugt nicht automatisch eine
deutsche Darstellung mit Punkten für Tausender und Komma als Dezimaltrenner.
Neu in Python 3.14: auch den Nachkommateil gruppieren
Seit Python 3.14 können Komma und Unterstrich auch im Nachkommateil von Fließkommadarstellungen als Gruppierungszeichen verwendet werden.
>>> f"{123456.123456:._f}"
'123456.123_456'
>>> f"{123456.123456:.,f}"
'123456.123,456'
Die Gruppierung hinter der Breite betrifft den ganzzahligen Teil, die Gruppierung hinter der Präzision den Nachkommateil.
Beides lässt sich kombinieren:
>>> f"{123456.123456:_._f}"
'123_456.123_456'
Oder mit unterschiedlichen Zeichen:
>>> f"{123456.123456:,.6_f}"
'123,456.123_456'
Das ist eher für technische Darstellungen als für normale Benutzerausgaben gedacht, zeigt aber ganz gut, wie weit die Mini-Sprache inzwischen geht.
Locale-abhängige Zahlen mit n
Für Zahlen existiert außerdem der Formattyp n. Er richtet sich nach der
aktuell eingestellten Locale:
import locale
locale.setlocale(locale.LC_NUMERIC, "")
zahl = 1234567
print(f"{zahl:n}")
Wie die Ausgabe aussieht, hängt von der aktiven Locale ab. Bei float entspricht
n dem Typ g, also sechs signifikanten Stellen.
Wichtig dabei: Python übernimmt für LC_NUMERIC nicht einfach in jeder
Situation automatisch die regionale Einstellung des Betriebssystems. Wenn Du
Locale-abhängig formatieren möchtest, musst Du Dich bewusst um die Locale
kümmern.
Für viele Anwendungen sind spezialisierte Internationalisierungs-Libraries
ohnehin die bessere Wahl. Für kleine Programme und Terminaltools ist n aber
gut zu kennen.
Vorzeichen steuern
Mit + erzwingst Du ein sichtbares Vorzeichen für positive und negative Werte:
>>> f"{42:+}"
'+42'
>>> f"{-42:+}"
'-42'
Das eignet sich beispielsweise für Veränderungen des Player States:
heilung = 20
schaden = -15
print(f"HP: {heilung:+}")
print(f"HP: {schaden:+}")
Ausgabe:
HP: +20
HP: -15
Ein Leerzeichen als Sign-Option reserviert bei positiven Zahlen den Platz des fehlenden Pluszeichens:
>>> f"{42: }"
' 42'
>>> f"{-42: }"
'-42'
Dadurch lassen sich positive und negative Werte leichter untereinander ausrichten.
Das explizite - als Sign-Option bedeutet dagegen schlicht: Nur negative
Zahlen bekommen ein Vorzeichen. Das ist ohnehin das Standardverhalten.
Negative Null mit z beseitigen
Fließkommazahlen können eine negative Null besitzen:
>>> f"{-0.0001:.2f}"
'-0.00'
Mathematisch ist die Anzeige -0.00 häufig eher störend als hilfreich.
Seit Python 3.11 gibt es dafür die Option z:
>>> f"{-0.0001:z.2f}"
'0.00'
z wandelt eine negative Null nach dem Runden in eine positive Null um.
Das ist beispielsweise bei Messwerten oder berechneten Abweichungen praktisch, bei denen ein sehr kleiner negativer Wert auf die gewählte Präzision zu Null gerundet wird.
Andere Zahlensysteme
Ganzzahlen können direkt binär, oktal oder hexadezimal ausgegeben werden:
| Typ | Darstellung |
|---|---|
b |
binär |
o |
oktal |
x |
hexadezimal mit Kleinbuchstaben |
X |
hexadezimal mit Großbuchstaben |
>>> f"{255:b}"
'11111111'
>>> f"{255:o}"
'377'
>>> f"{255:x}"
'ff'
>>> f"{255:X}"
'FF'
Mit # ergänzt Python das für das jeweilige Zahlensystem übliche Präfix:
>>> f"{255:#b}"
'0b11111111'
>>> f"{255:#o}"
'0o377'
>>> f"{255:#x}"
'0xff'
>>> f"{255:#X}"
'0XFF'
Auch Nullauffüllung lässt sich damit kombinieren:
>>> f"{255:#010b}"
'0b11111111'
Die gesamte Ausgabe ist mindestens zehn Zeichen breit, wobei das Präfix 0b
mitgezählt wird.
Mit _ lassen sich auch diese Zahlen gruppieren. Bei Binär-, Oktal- und
Hexadezimaldarstellung gruppiert Python dabei in Viererblöcken:
>>> f"{65535:#_x}"
'0xffff'
Bei größeren Werten wird die Gruppierung sichtbarer:
>>> f"{4294967295:#_x}"
'0xffff_ffff'
Ein Integer als Zeichen mit c
Der Formattyp c interpretiert eine Ganzzahl als Unicode-Codepoint:
>>> f"{65:c}"
'A'
>>> f"{9731:c}"
'☃'
Das ist im normalen Anwendungscode selten nötig, gehört aber ebenfalls zu den Integer-Formattypen.
Im Grunde entspricht die Idee hier chr(...):
chr(65)
ergibt ebenfalls "A".
Wissenschaftliche Schreibweise
Für sehr große oder kleine Zahlen gibt es e und E:
>>> f"{1234567:e}"
'1.234567e+06'
>>> f"{1234567:.2e}"
'1.23e+06'
>>> f"{1234567:.2E}"
'1.23E+06'
e verwendet ein kleines, E ein großes Zeichen für den Exponenten.
Der Typ g wählt abhängig von Größenordnung und Präzision zwischen normaler und
wissenschaftlicher Darstellung:
>>> f"{1234567:.4g}"
'1.235e+06'
>>> f"{12.34567:.4g}"
'12.35'
Bei g beschreibt die Präzision die Zahl der signifikanten Stellen und nicht
einfach die Nachkommastellen.
Für normale Benutzeroberflächen ist f deshalb oft vorhersehbarer. g eignet
sich gut für technische Ausgaben mit Werten aus sehr unterschiedlichen
Größenordnungen.
Die Großbuchstabenvarianten F und G verhalten sich grundsätzlich wie f
und g, verwenden bei speziellen Fließkommawerten aber Großbuchstaben:
>>> f"{float('inf'):f}"
'inf'
>>> f"{float('inf'):F}"
'INF'
>>> f"{float('nan'):F}"
'NAN'
Debug-Syntax mit =
Seit Python 3.8 kannst Du hinter einem Ausdruck ein Gleichheitszeichen setzen:
hp = 75
print(f"{hp=}")
Ausgabe:
hp=75
Python gibt dabei den Ausdruck selbst und direkt dahinter seinen Wert aus.
Das funktioniert auch mit längeren Ausdrücken:
hp = 75
max_hp = 100
print(f"{max_hp - hp=}")
print(f"{hp * 2=}")
Ausgabe:
max_hp - hp=25
hp * 2=150
Für einen schnellen Blick auf Zwischenwerte ist das angenehmer als:
print("hp =", hp)
Standardmäßig repr(...)
Die Debug-Syntax besitzt eine kleine Besonderheit.
Ein normales Replacement Field verwendet ohne weitere Angaben im Wesentlichen die normale String-Darstellung:
name = "Karl"
print(f"{name}")
Ausgabe:
Karl
Bei der Debug-Syntax verwendet Python ohne Formatangabe standardmäßig
repr(...):
name = "Karl"
print(f"{name=}")
Ausgabe:
name='Karl'
Bei Strings sind dadurch beispielsweise Anführungszeichen, Escape-Sequenzen und unsichtbare Leerzeichen leichter zu erkennen.
Leerzeichen bleiben erhalten
Die Debug-Syntax bewahrt die Schreibweise des Ausdrucks mitsamt relevanten Leerzeichen:
hp = 75
print(f"{hp = }")
Ausgabe:
hp = 75
Auch ungewöhnlichere Abstände bleiben sichtbar:
>>> f"{ hp * 2 = }"
' hp * 2 = 150'
Das = ist damit tatsächlich eine Art selbstbeschreibende Ausgabe des
Quelltextes.
Debug-Syntax mit Formatangabe
Eine Formatangabe lässt sich direkt anhängen:
trefferquote = 0.7564
print(f"{trefferquote=:.1%}")
Ausgabe:
trefferquote=75.6%
Auch Conversions funktionieren:
name = "Karl\n"
print(f"{name=!r}")
Ausgabe:
name='Karl\n'
Conversions mit !s, !r und !a
Vor dem Doppelpunkt kann eine Conversion stehen:
f"{ausdruck!conversion:formatangabe}"
Python kennt drei Varianten:
| Conversion | Verwendete Funktion |
|---|---|
!s |
str(...) |
!r |
repr(...) |
!a |
ascii(...) |
Die Conversion findet statt, bevor die Formatangabe auf das Ergebnis angewendet wird.
!s: normale String-Darstellung
!s verwendet str(...):
name = "Karl"
print(f"{name!s}")
Bei einem normalen String unterscheidet sich das Ergebnis nicht von:
print(f"{name}")
Interessanter wird der Unterschied bei Objekten, deren str(...)- und
repr(...)-Darstellung verschieden sind.
!r: Repräsentation für Entwickler
!r verwendet repr(...):
name = "Karl "
print(f"{name!r}")
Ausgabe:
'Karl '
Die Anführungszeichen machen deutlich, dass der Wert ein String ist. Außerdem ist das Leerzeichen vor dem schließenden Anführungszeichen leichter zu erkennen.
Escape-Sequenzen bleiben ebenfalls sichtbar:
text = "Zeile 1\nZeile 2"
print(f"{text!r}")
Ausgabe:
'Zeile 1\nZeile 2'
Deshalb ist !r vor allem beim Debugging nützlich.
!a: Nicht-ASCII-Zeichen escapen
!a verwendet ascii(...):
name = "Jörg"
print(f"{name!a}")
Ausgabe:
'J\xf6rg'
Nicht-ASCII-Zeichen werden dabei durch Escape-Sequenzen dargestellt.
Im normalen Anwendungscode braucht man das selten. Beim Untersuchen von Encoding- oder Zeichensatzproblemen kann es trotzdem hilfreich sein.
Dynamische Breite und Präzision
Formatangaben können selbst Replacement Fields enthalten. Dadurch lassen sich Breite und Präzision zur Laufzeit bestimmen.
wert = 3.14159
stellen = 3
print(f"{wert:.{stellen}f}")
Ausgabe:
3.142
Zuerst wird
{stellen}
ausgewertet. Daraus entsteht in diesem Fall die Formatangabe .3f, die danach
auf wert angewendet wird.
Dasselbe funktioniert mit der Breite:
name = "Karl"
breite = 12
print(f"|{name:^{breite}}|")
Ausgabe:
| Karl |
Breite und Präzision können gleichzeitig dynamisch sein:
wert = 3.14159
breite = 10
stellen = 2
print(f"{wert:{breite}.{stellen}f}")
Ausgabe:
3.14
Das ist beispielsweise sinnvoll, wenn der Benutzer die Zahl der Nachkommastellen auswählen kann oder eine Spaltenbreite erst anhand der Daten berechnet wird.
Nicht beliebig tief verschachteln
Die äußere Formatangabe darf Replacement Fields enthalten:
f"{wert:{breite}.{stellen}f}"
Ein solches verschachteltes Feld darf selbst eine Conversion oder Formatangabe besitzen. Weitere Replacement Fields darin sind laut Dokumentation aber nicht zulässig.
Für kompliziertere Fälle ist es ohnehin leichter lesbar, die Formatangabe vorher zusammenzubauen:
stellen = 2
formatangabe = f".{stellen}f"
wert = 3.14159
print(f"{wert:{formatangabe}}")
Der Inhalt von formatangabe ist hier schlicht:
.2f
Datum und Uhrzeit formatieren
Objekte können selbst definieren, was ihre Formatangabe bedeutet. Ein wichtiges
Beispiel sind date- und datetime-Objekte:
from datetime import datetime
zeitpunkt = datetime(2026, 6, 23, 14, 30)
print(f"{zeitpunkt:%Y-%m-%d %H:%M}")
Ausgabe:
2026-06-23 14:30
Die Format-Codes entsprechen denen von strftime(...).
Einige häufig verwendete Varianten:
| Code | Bedeutung | Beispiel |
|---|---|---|
%Y |
vierstelliges Jahr | 2026 |
%m |
Monat | 06 |
%d |
Tag | 23 |
%H |
Stunde im 24-Stunden-Format | 14 |
%M |
Minute | 30 |
%S |
Sekunde | 00 |
%A |
ausgeschriebener Wochentag | abhängig von Locale |
%B |
ausgeschriebener Monat | abhängig von Locale |
Eine numerische deutsche Datumsdarstellung lässt sich beispielsweise so erzeugen:
print(f"{zeitpunkt:%d.%m.%Y um %H:%M Uhr}")
Ausgabe:
23.06.2026 um 14:30 Uhr
Hier sieht man auch, warum man die Format-Mini-Sprache nicht als starre Liste von Codes verstehen sollte.
Ein float interpretiert
.2f
nach seinen Regeln. Ein datetime-Objekt interpretiert
%d.%m.%Y
nach seinen eigenen.
Was __format__(...) damit zu tun hat
Hinter der Formatierung steckt das __format__()-Protokoll.
Der Aufruf
format(wert, ".2f")
führt für den jeweiligen Typ sinngemäß dessen Formatierung mit .2f aus.
Deshalb kannst Du dieselbe Mini-Sprache außerhalb von f-Strings verwenden:
>>> format(3.14159, ".2f")
'3.14'
und:
>>> f"{3.14159:.2f}"
'3.14'
ergeben dasselbe formatierte Resultat.
Eigene Klassen können __format__(...) implementieren und damit eigene
Formatangaben verstehen.
class Spieler:
def __init__(self, name, hp):
self.name = name
self.hp = hp
def __format__(self, formatangabe):
if formatangabe == "kurz":
return f"{self.name} ({self.hp} HP)"
return self.name
spieler = Spieler("Karl", 75)
print(f"{spieler}")
print(f"{spieler:kurz}")
Ausgabe:
Karl
Karl (75 HP)
kurz ist kein eingebauter Python-Formattyp. Unsere Klasse entscheidet selbst,
dass diese Angabe gültig ist und was sie bedeutet.
Darum funktionieren beispielsweise auch die %Y-Codes von datetime, obwohl
sie bei einem normalen String oder Integer keinen Sinn ergeben.
Geschweifte Klammern ausgeben
Geschweifte Klammern besitzen in einem f-String eine besondere Bedeutung:
name = "Karl"
print(f"Hallo, {name}!")
Möchtest Du stattdessen eine echte Klammer ausgeben, verdoppelst Du sie:
print(f"Eine Klammer sieht so aus: {{")
print(f"Ein Paar sieht so aus: {{}}")
Ausgabe:
Eine Klammer sieht so aus: {
Ein Paar sieht so aus: {}
Das funktioniert genauso mit schließenden Klammern:
print(f"{{links}} und {{rechts}}")
Ausgabe:
{links} und {rechts}
Ein Backslash ersetzt diese Schreibweise nicht:
\{
ist kein Escape für eine f-String-Klammer.
Mehrzeilige f-Strings
Wie normale Strings können auch f-Strings mit dreifachen Anführungszeichen über mehrere Zeilen gehen:
name = "Karl"
hp = 75
gold = 50
status = f"""
Name: {name}
HP: {hp}
Gold: {gold}
"""
print(status)
Dabei gehört der Zeilenumbruch direkt hinter """ zum String.
Wenn Du diese zusätzliche Leerzeile nicht möchtest, kannst Du mehrere String-Literale innerhalb von Klammern verwenden:
status = (
f"Name: {name}\n"
f"HP: {hp}\n"
f"Gold: {gold}"
)
Nebeneinanderstehende String-Literale werden von Python automatisch zusammengefügt. Jeder einzelne Teil kann dabei ein f-String sein.
Gerade bei längeren Texten ist diese Variante häufig einfacher zu kontrollieren als ein eingerückter Triple-Quoted String.
Was sich mit Python 3.12 geändert hat
f-Strings existieren seit Python 3.6. Ihre ursprüngliche Implementierung hatte allerdings einige syntaktische Einschränkungen, die normale Python-Ausdrücke nicht besaßen.
Mit Python 3.12 wurde die Grammatik durch PEP 701 in den normalen Python-Parser integriert. Dadurch sind mehrere dieser Sonderregeln verschwunden.
Dieselben Anführungszeichen innen und außen
Seit Python 3.12 darf ein String innerhalb des Replacement Fields denselben Anführungszeichentyp verwenden wie der äußere f-String:
spieler = {
"name": "Karl",
}
print(f"{spieler["name"]}")
Das ist gültiges Python.
Vor Python 3.12 führte diese Schreibweise zu einem SyntaxError. Man musste
beispielsweise außen doppelte und innen einfache Anführungszeichen verwenden:
print(f"{spieler['name']}")
Diese Variante funktioniert weiterhin und ist für viele Fälle leichter zu lesen.
Backslashes innerhalb eines Ausdrucks
Seit Python 3.12 dürfen Backslashes innerhalb des Ausdrucks genauso verwendet werden wie in anderen Python-Ausdrücken:
zeilen = ["Halle", "Bibliothek", "Krypta"]
print(f"Räume:\n{"\n".join(zeilen)}")
Ausgabe:
Räume:
Halle
Bibliothek
Krypta
Vor Python 3.12 war der Ausdruck
"\n".join(zeilen)
innerhalb eines f-Strings wegen des Backslashes nicht zulässig.
Mehrzeilige Ausdrücke
Auch ein Replacement Field selbst darf sich seit Python 3.12 natürlicher über mehrere Zeilen erstrecken:
hp = 75
max_hp = 100
text = f"""Fehlende HP: {
max_hp - hp
}"""
print(text)
Ausgabe:
Fehlende HP: 25
Kommentare innerhalb des Ausdrucks
Kommentare sind ebenfalls erlaubt:
hp = 75
max_hp = 100
text = f"""Fehlende HP: {
max_hp - hp # Differenz zum Maximum
}"""
print(text)
Sobald innerhalb eines Replacement Fields ein # beginnt, gilt der Rest dieser
Zeile als Kommentar. Deshalb muss die schließende } in einer späteren Zeile
stehen.
f-Strings lassen sich freier verschachteln
Seit Python 3.12 können auch verschachtelte f-Strings denselben Anführungszeichentyp wiederverwenden:
name = "Karl"
text = f"Spieler: {f"{name:^10}"}"
print(text)
Das ist syntaktisch gültig.
Ob es lesbar ist, steht auf einem anderen Blatt.
Meist ist eine Zwischenvariable verständlicher:
name = "Karl"
formatierter_name = f"{name:^10}"
text = f"Spieler: {formatierter_name}"
PEP 701 schreibt nicht vor, dass jede Python-Implementierung beliebig tief verschachtelte f-Strings unterstützen muss. Portabler Code sollte nicht mehr als fünf Ebenen voraussetzen. CPython selbst begrenzt die Verschachtelung derzeit nicht ausdrücklich.
Spätestens weit vor fünf Ebenen dürfte allerdings die Lesbarkeit das größere Problem sein.
f-Strings werden sofort ausgewertet
Ein f-String ist kein dauerhaft mit seinen Variablen verbundenes Template.
name = "Karl"
text = f"Hallo, {name}!"
text enthält danach nur noch:
Hallo, Karl!
Wenn Du anschließend name änderst:
name = "Lara"
ändert sich text nicht:
print(text)
Ausgabe:
Hallo, Karl!
Der f-String müsste erneut ausgewertet werden:
text = f"Hallo, {name}!"
Erst jetzt enthält text:
Hallo, Lara!
Replacement Fields werden also beim Erzeugen des f-Strings ausgewertet, nicht erst irgendwann später bei dessen Ausgabe.
f-Strings sind keine Sicherheitsfunktion
f-Strings kümmern sich um String-Erzeugung und Formatierung. Sie wissen nichts über SQL, Shell-Syntax, HTML oder andere Kontexte.
Deshalb ist so etwas bei Benutzereingaben keine gute Idee:
sql = f"SELECT * FROM spieler WHERE name = '{name}'"
Der f-String setzt den Wert lediglich ein. Er escaped ihn nicht passend für eine Datenbank.
Für SQL verwendest Du die Parameterübergabe des Datenbanktreibers.
Dasselbe Prinzip gilt für Shell-Kommandos. Statt einen Befehl aus ungeprüften
Werten zusammenzubauen, solltest Du bei subprocess nach Möglichkeit
Argumentlisten verwenden und keine Shell dazwischenschalten.
f-Strings sind ein sehr gutes Werkzeug zur Darstellung von Werten. Für die sichere Einbettung in andere Sprachen sind die jeweiligen APIs zuständig.
Stolperfallen
- Das
fvergessen: Ohne Präfix bleibt das Replacement Field normaler Text.
python
name = "Karl"
print("{name}")
Ausgabe:
console
{name}
-
Eine einzelne geschweifte Klammer ausgeben: Für eine sichtbare Klammer brauchst Du
{{beziehungsweise}}. -
Breite mit maximaler Länge verwechseln:
{text:10}reserviert mindestens zehn Zeichen. Ein längerer String wird nicht abgeschnitten. Dafür dient bei Strings die Präzision, beispielsweise{text:.10}. -
Präzision bei
fundggleich interpretieren: Bei.2fsind zwei Nachkommastellen gemeint. Bei.2ggeht es um zwei signifikante Stellen. -
Von
:,auf die Locale schließen::,verwendet fest ein Komma zur Gruppierung und einen Punkt als Dezimaltrennzeichen. Für Locale-abhängige Darstellung gibt es unter anderem den Typn. -
%mit bereits umgerechneten Prozentwerten verwenden:{0.25:.0%}ergibt25%. Wer vorher schon mit100multipliziert, bekommt entsprechend2500%. -
Zu viel Logik in den f-String schreiben: Ein gültiger Python-Ausdruck ist nicht automatisch ein gut lesbarer Ausdruck.
-
Neue Syntax mit alten Python-Versionen verwenden: Wiederverwendete Anführungszeichen, Backslashes und Kommentare innerhalb der Replacement Fields benötigen mindestens Python 3.12.
-
Nachkommateile gruppieren: Schreibweisen wie
._foder.6_fbenötigen Python 3.14. -
Ungeprüfte Daten in SQL oder Shell-Kommandos einsetzen: f-Strings führen kein kontextabhängiges Escaping durch.
Kompakte Übersicht
| Aufgabe | Beispiel |
|---|---|
| Wert einsetzen | f"{name}" |
| Ausdruck auswerten | f"{hp * 2}" |
| zwei Nachkommastellen | f"{wert:.2f}" |
| String auf 10 Zeichen kürzen | f"{text:.10}" |
| Mindestbreite | f"{wert:10}" |
| linksbündig | f"{text:<10}" |
| rechtsbündig | f"{text:>10}" |
| zentriert | f"{text:^10}" |
| eigenes Füllzeichen | f"{text:-^20}" |
| führende Nullen | f"{nummer:04}" |
| Tausendertrenner | f"{zahl:,}" |
| Gruppierung Nachkommateil | f"{zahl:.6_f}" |
| Prozent | f"{anteil:.1%}" |
| Vorzeichen | f"{wert:+}" |
| negative Null entfernen | f"{wert:z.2f}" |
| Locale-abhängige Zahl | f"{wert:n}" |
| binär | f"{wert:#b}" |
| hexadezimal | f"{wert:#x}" |
| Unicode-Zeichen aus Integer | f"{wert:c}" |
| wissenschaftliche Schreibweise | f"{wert:.2e}" |
| Debug-Syntax | f"{wert=}" |
repr(...) verwenden |
f"{wert!r}" |
| dynamische Präzision | f"{wert:.{stellen}f}" |
| dynamische Breite | f"{wert:{breite}}" |
| Datum formatieren | f"{datum:%d.%m.%Y}" |
| sichtbare Klammern | f"{{wert}}" |
0 Kommentare
Noch keine Kommentare. Sei der/die Erste!
Anmelden um einen Kommentar zu hinterlassen.