Vergleich der String-Formatierung

Das Zen von Python sagt,
„Es sollte einen – und vorzugsweise nur einen – offensichtlichen Weg geben, es zu tun.“

Und doch gibt es drei Hauptmethoden für die String-Formatierung in Python. In diesem Artikel werden diese drei Methoden miteinander verglichen und man erhält Erkenntnisse und Empfehlungen, welche Methode in der jeweiligen Situation die beste ist.

Im Folgenden wird man:

  • die alte Formatierung im C-Stil mit % kennenlernen;
  • die String-Methode .format kennenlernen;
  • das Python 3.6+ Feature der literalen String-Interpolation und f-strings kennenlernen;
  • die wichtigsten Unterschiede zwischen den einzelnen Arten der String-Formatierung zu verstehen; und sehen, wo jede Art der String-Formatierung wirklich glänzt.

Einleitende Beispiele

>>> language = "Python"
alter Stil (C-Stil-Formatierung):
>>> "%s ist super!" % language
'Python ist super!'
neuer Stil (String-Methode .format):
>>> "{} ist super!".format(language)
'Python ist super!'
moderner Stil ab Python 3.6+ (f-String):
>>> f"{language} ist super!"
'Python ist super!'


# C-Stil-Formatierung:
def language_info_cstyle(language, users_estimate):
    return (
        "%s ist super! Wusstest Du, dass %s über %d User hat?!" %
        (language, language, users_estimate)
    )

# String-Methode .format:
def language_info_format(language, users_estimate):
    return "{} ist super! Wusstest Du, dass {} über {} Uuser hat?!".format(
        language, language, users_estimate
    )

# f-Strings, ab Python 3.6+:
def language_info_fstring(language, users_estimate):
    return (
        f"{language} ist super! Wusstest Du, dass {language}" +
        f" über {users_estimate} User hat?!"
    )

C-Stil-Formatierung

Die Formatierung im C-Stil, die es schon länger gibt, zeichnet sich durch eine Reihe von Prozentzeichen („%“) aus, die in den „Vorlage“-Strings auftauchen. (Mit „Vorlage-Strings“ sind die Strings gemeint, in denen die Lücken ausgefüllt werden sollen)

Diese Prozentzeichen geben die Stellen an, an denen die Informationen stehen sollen, und das darauf folgende Zeichen (z.B. „%s“ oder „%d“) bestimmt, wie die übergebenen Informationen behandelt werden.

Außerdem wird die Formatierung durch den binären Operator % vorgenommen: Links steht die Vorlagenzeichenkette, und rechts stehen alle Informationen, die übergeben werden sollen.

String-Methode .format

Die String-Methode .format ist, wie der Name schon sagt, eine Methode vom Typ String. Das bedeutet, dass man typischerweise eine Formatzeichenkette hat und, wenn man Zugriff auf die fehlenden Informationen hat, einfach die Methode .format für diese Zeichenkette aufruft.

Zeichenketten, die die Methode .format zur Formatierung verwenden, sind in der Regel durch eine Reihe geschweifter Klammern „{}“ innerhalb der Zeichenkette gekennzeichnet. Häufig wird die Methode .format auch dort aufgerufen, wo das Stringliteral definiert ist.

f-Strings

Bei der Interpolation von Zeichenketten werden Werte in Zeichenketten interpoliert.

Man beachte die Definition des Wortes „interpolieren“:
Verb: interpolieren – etwas (andersartiges) in etwas anderes einfügen.

Genau das macht diese Technik: Sie fügt die zusätzlichen Werte direkt in die Vorlagenzeichenkette ein.

Wenn man von „f-strings“ spricht, ist damit auch diese Technik gemeint. Das liegt daran, dass man der Zeichenkette ein f voranstellen muss, um die Zeichenketteninterpolation zu verwenden.

Literale String-Interpolation ist (eindeutig) durch das f-Präfix an den String-Literalen und auch durch die geschweiften Klammern „{}“ innerhalb des Strings gekennzeichnet. Anders als bei der String-Methode .format haben die geschweiften Klammern immer einen „Inhalt“.

Nachdem wir nun einen Blick auf die drei String-Formatierungsmethoden geworfen haben, betrachten wir eine Reihe von verschiedenen (einfachen) Szenarien und schauen wie die Formatierung mit den drei Optionen funktionieren würde.

Wie wir sehen werden, wird die Formatierung im C-Stil fast immer klobiger und weniger elegant aussehen, was helfen sollte zu erkennen, dass f-Strings und die String-Methode .format der bessere Weg sind.

Ausrichtung

Wenn wir viele Werte über viele Zeilen hinweg formatieren müssen, um z. B. eine tabellenähnliche Ausgabe anzuzeigen, möchten wir vielleicht alle Werte ausrichten und entsprechend auffüllen. Dies ist einer der Hauptanwendungsfälle, in denen die String-Formatierung glänzt.

language = "Python"

"%-10s" % language

"{:<10}".format(language)

f"{language:<10}"

# Das Ergebnis ist 
'Python    '

Der C-Stil ist standardmäßig rechtsbündig, während .format und f-Strings linksbündig sind. Daher hätten wir auch schreiben können
„{:10}“.format(lang)
f“{lang:10}“
und wir würden immer noch das gleiche Ergebnis erhalten. Um einen Vergleich zu ermöglichen, wurde jedoch das < für die linke Ausrichtung eingefügt.

Die Formatierung im Stil von C kann das nicht, aber die beiden modernen Methoden können ^ verwenden, um die Ausgabe in der Mitte auszurichten:
„{:^10}“.format(lang)
f“{lang:^10}“

Das Ergebnis ist
‚ Python ‚

Um rechtsbündig auszurichten, verwendet man > für die modernen Methoden, und für die Formatierung im C-Stil verwendet man kein Kennzeichen.
In den modernen Methoden kann man <^> für die Ausrichtung verwenden, die Spitze des Pfeils zeigt dabei auf die Ausrichtungsrichtung.

Benannte Platzhalter

Bei längeren Strings oder Strings mit vielen auszufüllenden Slots kann es hilfreich sein, Platzhalter-Strings anstelle des Symbols für die Stringformatierung einzufügen. Bei f-Zeichenfolgen geschieht dies mehr oder weniger automatisch, aber auch die Formatierung im C-Stil und .format unterstützen dies:

name, alter = "Hans", 73

"%(name)s ist %(alter)d Jahre alt." % {"name": name, "alter": alter}

"{name} ist {alter} Jahre alt.".format(name=name, alter=alter)

f"{name} ist {alter} Jahre alt."

# Das Ergebnis ist
'Hans ist 73 Jahre alt.'

Zugriff auf verschachtelte Datenstrukturen

Betrachten wir noch einmal das obige Beispiel, aber stellen wir uns vor, dass der Name und das Alter tatsächlich in einem Wörterbuch gespeichert wurden.

In diesem Fall sind die Formatierung im alten Stil und die String-Methode .format besonders praktisch:

data = {"name": "Hans", "alter": 73}

"%(name)s ist %(alter)d JAhre alt." % data

"{data[name]} ist {data[alter]} Jahre alt.".format(data=data)
# oder
"{name} is {alter} Jahre alt.".format(**data)

f"{data['name']} ist {data['alter']} Jahre alt."

Die erste Verwendung der String-Methode .format zeigt ein interessantes Feature, das die Formatierung mit .format ermöglicht: Die formatierten Objekte können indiziert werden und es kann auch auf ihre Attribute zugegriffen werden.

Parametrisierte Formatierung

Manchmal möchte man eine Zeichenfolge formatieren, aber die genaue Formatierung ist dynamisch: Man möchte zum Beispiel etwas mit variabler Breite drucken, und deshalb sollte sich die Breite an das längste Element in einer Folge anpassen.

Nehmen wir an, man hat eine Liste von Unternehmen und ihren Herkunftsländern, die man ausrichten möchte:

data = [("Toyota", "Japan"), ("Ford", "USA")]

for brand, country in data:
    print(f"{brand:>7}, {country:>8}")

# Das Ergebnis ist
 Toyota,    Japan
   Ford,      USA

Was passiert, wenn wir jetzt ein Unternehmen mit einem längeren Namen aufnehmen?

data = [("Toyota", "Japan"), ("Ford", "USA"), ("Lamborghini", "Italien")]

for brand, country in data:
    print(f"{brand:>7}, {country:>8}")

# Das Ergebnis ist
 Toyota,    Japan
   Ford,      USA
Lamborghini,    Italien

Die Ausgabe ist nicht mehr ausgerichtet, weil das Wort „Lamborghini“ nicht in die angegebene Breite von 7 passt. Daher müssen wir die maximalen Längen dynamisch berechnen und diese Werte verwenden, um die richtige Formatspezifikation zu erstellen. Hier kommt die Parametrisierung der Formatspezifikation ins Spiel:

data = [("Toyota", "Japan"), ("Ford", "USA"), ("Lamborghini", "Italien")]
# berechne die benötigte Breite für die Formatierung
bw = 1 + max(len(brand) for brand, _ in data)
cw = 1 + max(len(country) for _, country in data)

for brand, country in data:
    print(f"{brand:>{bw}}, {country:>{cw}}")

# Das Ergebnis ist
      Toyota,   Japan
        Ford,     USA
 Lamborghini,  Italien

Die Formatierung im alten Stil erlaubt nur die Parametrisierung der Breite des Feldes und der verwendeten Genauigkeit. Bei der String-Methode .format und bei f-strings kann die Parametrisierung mit allen Optionen für die Formatangabe verwendet werden.

month = "November"
prec = 3
value = 2.7182

"%.*s = %.*f" % (prec, month, prec, value)

"{:.{prec}} = {:.{prec}f}".format(month, value, prec=prec)

f"{month:.{prec}} = {value:.{prec}f}"

# Das Ergebnis ist jeweils
'Nov = 2.718'

Beispiele für eine nützliche Verwendung von f-String

Wie die obigen kleinen Codeschnipsel gezeigt haben, gibt es kaum einen Grund, die alte String-Formatierung zu verwenden. Man sollte aber daran denken, dass Konsistenz wichtig ist, so dass die alte Formatierung immer noch Sinn ergeben kann, wenn man eine alte Codebasis pflegt.
Ansonsten ist es besser, die String-Methode .format und/oder f-strings zu verwenden.
Nachfolgend sollen ein paar Anwendungsmuster zeigen, welche Art der String-Formatierung in diesen Fällen am besten funktioniert.

Vorteil
f-Strings sind sehr, sehr gut. Sie sind kurz in der Eingabe, haben gute Lokalitätseigenschaften (es ist leicht zu erkennen, was zur Formatierung dieses bestimmten Teils der Zeichenkette verwendet wird) und sie sind schnell.

Man sollte f-strings der Methode .format vorziehen, wenn man eine einfache Formatierung benötigt:

# Besser
f"{name!s} {name!r}"
f"{name:<10}"
f"{name} ist {alter} Jahre alt."
f"{name:^{weite}}"

# verwenden als `.format`
"{!s} {!r}".format(name, name)
"{:<10}".format(name)
"{name} is {alter} Jahre alt.".format(name=name, alter=alter)
"{:^{weite}}".format(name, weite=weite)

Daten in einem Dictionary

Wenn sich alle Formatierungsdaten bereits in einem Wörterbuch befinden, ist die Verwendung der String-Methode .format möglicherweise die beste Lösung.

Dies gilt insbesondere, wenn die Schlüssel des Wörterbuchs Zeichenketten sind. In diesem Fall sieht die Verwendung der String-Methode .format fast so aus wie die Verwendung von f-strings! Wenn die Daten jedoch in einem Wörterbuch stehen, ist die Verwendung von f-strings im Vergleich zur Verwendung von ** in .format viel ausführlicher:

data = {"name": "Hans", "alter": 73}

# übersichtlich
"{name} ist {alter} Jahre alt.".format(**data)

# aufwendiger
f"{data['name']} ist {data['alter']} Jahre alt."

„Aufgeschobene / Verzögerte“ Formatierung

Wenn man eine Formatierungszeichenfolge zuerst erstellen und erst später formatieren mmöchte, kann man f-strings nicht verwenden!

In diesem Fall ist die Verwendung der Methode .format wahrscheinlich die beste Lösung.

Diese Art von Szenario kann z. B. bei Programmen auftreten, die in (vielen) verschiedenen Sprachen laufen:

def get_greeting(language):
    if language == "pt":
        return "Olá, {}!"
    else:
        return "Hello, {}!"

lang = input(" [en/pt] >> ")
name = input(" your name >> ")
get_greeting(lang).format(name)

Fazit

Hier ist die wichtigste Erkenntnis aus diesem Artikel:

„Man verwendet keine String-Formatierung im alten Stil: Man verwendet f-Strings, wann immer es möglich ist, und .format in den anderen Fällen.“

Den Artikel zusammengefasst:

  • Python hat drei eingebaute Arten der String-Formatierung;
  • Die Verwendung von .format und/oder f-strings ist gegenüber %-Formatierung vorzuziehen;
  • Man kann !s und !r verwenden, um anzugeben, welche Art der Zeichenkettendarstellung verwendet werden soll;
  • Die Ausrichtung kann mit den <^>-Spezifikationen vorgenommen werden;
  • Formatangaben können mit einer zusätzlichen Ebene von {} parametrisiert werden;
  • f-strings sind für die meisten Standardformatierungsaufgaben sehr gut geeignet;
  • Die Methode .format ist nützlich, wenn sich die Formatierungsdaten innerhalb eines Wörterbuchs befinden;
  • Für die verzögerte Formatierung von Zeichenketten funktionieren f-Zeichenketten nicht, d.h. .format ist die empfohlene Methode zur Formatierung von Zeichenketten.