2 Punkte von GN⁺ 2025-04-11 | 3 Kommentare | Auf WhatsApp teilen
  • PEP 750 führt in Python ein neues String-Literal ein: Template-Strings (t"...")
  • Als verallgemeinerte Form von f-strings erzeugen sie den Typ Template und ermöglichen die Vorverarbeitung von Strings und eingebetteten Werten vor der Kombination
  • Nützlich für Web-Templates, Sicherheitsprüfungen, DSLs (Domain-Specific Languages) und mehr

Beziehung zu anderen PEPs

  • f-strings wurden mit PEP 498 eingeführt, und ihre Syntax wurde in PEP 701 erweitert
  • PEP 501 schlug allgemeine Template-Strings (i-string) vor, wurde aber zurückgestellt
  • Das aktuelle PEP 750 ist eine vereinfachte und verallgemeinerte Form von PEP 501 und entwickelt die bestehenden Ideen weiter

Motivation und Notwendigkeit

  • f-strings sind einfach, können eingebettete Werte aber nicht vorab verarbeiten, was Sicherheitsprobleme verursachen kann
  • Es besteht das Risiko von Sicherheitslücken wie SQL-Injection und XSS-Angriffen
  • Mit Template-Strings lassen sich eingebettete Werte vorab verarbeiten und dadurch sicherer verwenden

Beispiel:

  • evil = "<script>alert('evil')</script>"
  • template = t"<p>{evil}</p>"
  • assert html(template) == "<p>&lt;script&gt;alert('evil')&lt;/script&gt;</p>"

Spezifikation der Template-Strings

Template-String-Literale

  • Werden mit dem Präfix t oder T definiert
  • Werden zu string.templatelib.Template ausgewertet
  • Unterstützen eine f-string-ähnliche Syntax, auch verschachtelt
  • Können mit dem Präfix r kombiniert werden (rt, tr)
  • Können nicht mit den Präfixen u oder b kombiniert werden
  • f-strings und Template-Strings können nicht gemischt verwendet werden

Typ Template

  • Ein unveränderlicher Typ mit den folgenden Eigenschaften:
    • strings: Tupel von String-Fragmenten
    • interpolations: Tupel von Einbettungsobjekten
    • values: Wertetupel der Einbettungen
    • __iter__(): Iterator, der Strings und Einbettungen der Reihe nach zurückgibt

Typ Interpolation

  • value: das ausgewertete Ergebnis
  • expression: die ursprüngliche String-Darstellung des Einbettungsausdrucks
  • conversion: Art der Konvertierung (r, s, a oder None)
  • format_spec: Format-String

Beispiel:

  • name = "World"
  • template = t"Hello {name!r}"
  • assert template.interpolations[0].conversion == "r"

Debug-Spezifizierer =

  • t"{value=}" wird als t"value={value!r}" interpretiert
  • Auch Leerzeichen bleiben unverändert erhalten (t"{value = }""value = {value!r}")

Verkettung von Template-Strings

  • Mit dem +-Operator können Template und str sowie zwei Template-Objekte kombiniert werden
  • Das Ergebnis einer Verkettung ist immer vom Typ Template
  • Auch implizite String-Verkettung ist möglich (t"Hello " t"World")

Verarbeitung von Template-Strings

Beispiel: Funktion zur Groß-/Kleinschreibung

  • def lower_upper(template):
    • parts = []
    • for s in template:
      • if isinstance(s, str): parts.append(s.lower())
      • else: parts.append(str(s.value).upper())
    • return "".join(parts)

Beispiel: Verarbeitung wie bei f-strings implementieren

  • Mit einer Funktion f() lässt sich dasselbe Ergebnis wie mit f-strings erzeugen

Beispiel: strukturiertes Logging

  • Mit Template-Strings können Log-Nachrichten und strukturierte Werte gleichzeitig ausgegeben werden
  • Kann mit StructuredMessage oder einer Unterklasse von logging.Formatter implementiert werden

Beispiel: HTML-Template-Verarbeitung

  • Die Funktion html() behandelt Inhalte je nach Einfügeposition passend per Escaping oder als Attribut
  • Auch verschachtelte Templates werden unterstützt

Fortgeschrittene Nutzungsmuster

  • Die Verwendung von strukturellem Pattern Matching (match-Anweisung) wird empfohlen
  • Statische Strings können als Cache-Schlüssel dienen und effiziente Memoization ermöglichen
  • Verarbeitung durch Parsen in Zwischenrepräsentationen wie AST ist möglich
  • Für Lazy- oder Async-Auswertung können lambda und await verwendet werden

Beziehung zwischen Template-Strings und bestehenden Format-Strings

  • Template-Funktionen lassen sich ähnlich wie mit bestehendem .format() definieren
  • Auch from_format() zum Parsen externer Strings und zur Umwandlung in Template ist möglich

Kompatibilität, Sicherheit und Lernen

  • In älteren Python-Versionen kann ein Syntaxfehler auftreten
  • Die Verarbeitung über Templates erhöht die Sicherheit
  • Durch die f-string-ähnliche Syntax ist der Einstieg einfach

Warum ein neuer Template-Ansatz?

  • Bestehende Templates wie Jinja richten sich an Nutzeranpassungen oder Designer
  • Es braucht Unterstützung auf Sprachebene in Python, damit Entwickler Templates direkt handhaben können
  • So lassen sich Vorteile wie Ausdrucksstärke und Typprüfung nutzen

Zusammenfassung typischer Muster

  • Strukturelles Pattern Matching und Matching auf Untereigenschaften
  • Wiederverwendung von Templates wie Funktionen
  • Unterstützung verschachtelter Templates
  • Unterstützung für Lazy-/Async-Auswertung
  • Cache-Optimierung durch Trennung von statischen und dynamischen Teilen

Weitere Designüberlegungen

  • Templates werden nicht in Strings umgewandelt; __str__() ist nicht implementiert
  • Die zugehörigen Klassen werden im Modul string.templatelib bereitgestellt
  • Template und Interpolation werden anhand der Objektidentität verglichen
  • Die Operatoren == und < werden nicht unterstützt

Referenzimplementierung und Beispiele

Abgelehnte Ideen

  • Verwendung beliebiger Präfixe (my_tag"...")
  • Verzögerte Auswertung aller Einbettungsausdrücke
  • Implementierung als Protokoll
  • Neudefinition von __eq__ und __hash__
  • Vollständige Wiederherstellung des Original-Strings
  • Hinzufügen eines Typs Decoded
  • Unterstützung binärer Template-Strings
  • Möglichkeit zur Angabe von Formatarten ("html", "sql" usw.)
  • Einschränkung der String-Verkettung
  • Zulassen beliebiger Konverter (!x)

3 Kommentare

 
carnoxen 2025-04-11

Die zufriedenstellendste Formatierung gibt es meiner Meinung nach nur in JS und Python. Andere Sprachen sind eher so na ja...

 
kandk 2025-04-11

Es sollte einen klaren und möglichst nur einen offensichtlichen Weg geben, etwas zu tun. (There should be one-- and preferably only one --obvious way to do it.)

 
GN⁺ 2025-04-11
Hacker-News-Kommentare
  • Es ist interessant, wie verschiedene Sprachen mit String-Formatierung umgehen

    • Java versucht, f-/t-strings hinzuzufügen, hat dabei aber wegen eines Perfektionismus, der alle Probleme lösen will, Schwierigkeiten
    • Go-Entwickler scheinen dieses Problem kaum bedacht und weitgehend ignoriert zu haben
    • Python verfolgt einen ausgewogenen Ansatz, diskutiert neue Methoden der String-Formatierung und verwendet passend gewählte Implementierungen
    • Dem Ansatz von Python kann man kaum widersprechen, und man profitiert von .format(), f-strings und t-strings
  • Nick Humrich ist einer der Autoren, die PEP 501 umgeschrieben und t-strings eingeführt haben, und freut sich sehr über die Annahme dieses PEP

    • Er hat vor 4 Jahren mit der Arbeit an PEP 501 begonnen
  • Ich bin mir nicht sicher, ob ein Feature auf Sprachebene wirklich wertvoll ist

    • Dasselbe Ergebnis lässt sich mit einer Funktion erzielen, die ein f-string zurückgibt
    • Wenn man Injektionssicherheit will, kann man einen Tag-Typ und eine Sanitizing-Funktion verwenden, die einen String zurückgibt
    • Es ist kompakt, aber die Unterscheidung zwischen sofortiger und verzögerter Ausführung mit einem einzelnen Buchstaben könnte für Leute, die nicht mit Python vertraut sind, schwer lesbar sein
  • Ich mag f-strings, aber sie haben das Problem, dass man die Auswertung nicht verzögern kann

    • Es gibt Fälle, in denen man str.format verwenden muss, und das ist unpraktisch
  • Als Maintainer von lit-html finde ich die Ähnlichkeiten zu den getaggten Template-Literalen von JavaScript interessant

    • Die Art, wie die Template-Klasse von Python die JavaScript-Tag-Funktion und ihre Argumente trennt, ist ungewöhnlich
    • In verschachtelten Template-Strukturen könnte die Funktion html() unnötig sein
  • Ich hoffe, dass das, was getaggte Template-Literale in JavaScript für automatisches HTML-Escaping oder SQL-Parametrisierung nützlich macht, auch auf Python übertragbar ist

  • Es gibt die Meinung, Python verwandle sich in PHP

    • f-strings und t-strings erhöhen die Komplexität der Sprache
    • string.format halte ich für optimal, und % ist ebenfalls akzeptabel, da es schon lange verwendet wird
    • Ich wünschte, das Sprachteam würde sich auf Wichtigeres konzentrieren
  • Unzufriedenheit darüber, dass der Sprache ständig Neues hinzugefügt wird

    • Die Sprache fühlt sich an, als wäre sie von einem Komitee entworfen worden
  • Die Meinung, dass dieses PEP dem P1819 von C++ ähnelt

  • Die Meinung, dass der Code im PEP zu ausführlich ist

    • Python scheint eher übermäßige Unnötigkeit auszudrücken als ausführbaren Pseudocode
    • Im Vergleich zu Ruby ist Python-Code wortreicher