Aufwärmen · Aufgabe 1 von 7
Aufwärmen aus Modul A4 und der f-String-Lektion: Was gibt das aus?
from decimal import Decimal
price = Decimal("0.1") + Decimal("0.2")
print(f"{price} {0.1 + 0.2:.2f}")// A5.1 · ca. 30 Min. · Vertiefung
Nach dieser Lektion lesen Sie einen t-String als Template aus statischen Strings und Interpolationen, schreiben einen Renderer, der Konvertierungen und Formatangaben anwendet, und maskieren unsichere Werte, wo ein f-String sie vermischt hätte.
Lektion 1 von 5 in A5 Modernes Python und sicherer Code
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
from decimal import Decimal
price = Decimal("0.1") + Decimal("0.2")
print(f"{price} {0.1 + 0.2:.2f}")Vorhersagen · Aufgabe 2 von 7
name = "Ada"
greeting = t"Hello {name}!"
print(type(greeting).__name__, greeting.strings)Üben · Aufgabe 3 von 7
from string.templatelib import Interpolation
def shout(template):
parts = []
for part in template:
if isinstance(part, ____):
parts.append(str(part.value).upper())
else:
parts.append(part)
return "".join(parts)Üben · Aufgabe 4 von 7
x = "hi"
tpl = t"{x!r:>6}"
i = tpl.interpolations[0]
print(i.value, i.conversion, i.format_spec)Üben · Aufgabe 5 von 7
Denksport · Aufgabe 6 von 7
name = "Ada"
greeting = t"Hi {name}!"
print(greeting == "Hi Ada!", "".join(greeting.strings))Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
render_html() durchläuft einen t-String. Es wendet jede Konvertierung und Formatangabe an wie ein f-String, maskiert das Ergebnis mit html.escape und übernimmt das statische Markup unverändert. Der Nutzername enthält spitze Klammern, wie sie ein Angreifer versuchen würde. Die letzte Zeile zeigt zum Vergleich die f-String-Fassung: Sobald alles ein str ist, lassen sich Markup des Programmierers und Text des Nutzers nicht mehr unterscheiden.
main.py
import html
from string.templatelib import Interpolation, Template, convert
def render_html(template: Template) -> str:
"""Join a template, escaping every interpolated value for HTML."""
parts: list[str] = []
for part in template:
if isinstance(part, Interpolation):
value = convert(part.value, part.conversion)
text = format(value, part.format_spec)
parts.append(html.escape(text))
else:
parts.append(part) # static text, written by the programmer
return "".join(parts)
user = "Ada <admin>"
total = 1234.5
page = t"<p>Hello {user}, you owe {total:,.2f} EUR</p>"
print(page.strings)
print(page.values)
print(render_html(page))
print(f"<p>Hello {user}</p>") # an f-string has already merged the parts
Ausführen mit
python main.pyAusgabe
('<p>Hello ', ', you owe ', ' EUR</p>')
('Ada <admin>', 1234.5)
<p>Hello Ada <admin>, you owe 1,234.50 EUR</p>
<p>Hello Ada <admin></p>Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.
Übung 1 von 2
Vervollständigen Sie render(template) in main.py, sodass es genau das liefert, was der entsprechende f-String ergäbe. Der Starter setzt str(value) ein und ignoriert Konvertierung und Formatangabe. Wenden Sie part.conversion mit convert() an, danach part.format_spec mit format(). Die Tests vergleichen mit f-Strings, auch mit dem =-Spezifizierer.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.
convert(part.value, part.conversion) gibt den Wert bei conversion None unverändert zurück, sonst str(), repr() oder ascii() davon.
format(value, "") entspricht str(value), format(konvertiert, part.format_spec) funktioniert also auch ohne Angabe.
Der =-Spezifizierer braucht keinen eigenen Code: Der t-String schreibt "n=" schon in strings und setzt conversion auf "r".
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
from string.templatelib import Interpolation, Template, convert
def render(template: Template) -> str:
"""Render a template exactly like the matching f-string."""
out: list[str] = []
for part in template:
if isinstance(part, Interpolation):
value = convert(part.value, part.conversion)
out.append(format(value, part.format_spec))
else:
out.append(part)
return "".join(out)
Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.py
from string.templatelib import Interpolation, Template, convert
def render(template: Template) -> str:
"""Render a template exactly like the matching f-string."""
out: list[str] = []
for part in template:
if isinstance(part, Interpolation):
# Apply part.conversion (with convert) and part.format_spec (with format).
out.append(str(part.value))
else:
out.append(part)
return "".join(out)
test_main.py
from main import render
def test_plain():
"""Ein einfacher Wert wird unverändert eingesetzt"""
name = "Ada"
got = render(t"Hi {name}!")
assert got == "Hi Ada!", f"render lieferte {got!r}, erwartet 'Hi Ada!'"
def test_format_spec():
"""Die Formatangabe wird angewendet, wie im f-String"""
price = 3.14159
got = render(t"{price:.2f} EUR")
assert got == "3.14 EUR", f"render lieferte {got!r}: part.format_spec mit format() anwenden"
def test_conversion():
"""!r wird angewendet, wie im f-String"""
word = "hi"
got = render(t"say {word!r}")
assert got == "say 'hi'", f"render lieferte {got!r}: part.conversion mit convert() anwenden"
def test_same_as_fstring():
"""Der =-Spezifizierer und eine Breite ergeben dasselbe wie der f-String"""
n = 5
got = render(t"{n=} [{n:>4}]")
want = f"{n=} [{n:>4}]"
assert got == want, f"render lieferte {got!r}, der f-String ergibt {want!r}"
Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.
Programm ausführen:
python main.pyPrüfungen ausführen (learnrun.py muss im selben Ordner liegen):
python learnrun.py testlearnrun.py herunterladenÜbung 2 von 2
Vervollständigen Sie safe_html(template). Maskieren Sie jeden interpolierten Wert mit html.escape, nachdem Sie Konvertierung und Formatangabe angewendet haben, und lassen Sie das statische Markup unverändert. Ist das Argument kein Template, etwa der str aus einem f-String, lösen Sie TypeError aus: Ein Renderer, der einen str annimmt, wüsste nicht, welche Teile zu maskieren sind.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.
Beginnen Sie mit if not isinstance(template, Template): raise TypeError(...).
Durchlaufen Sie das Template. Eine Interpolation formatieren Sie wie in der vorigen Übung und hängen html.escape(text) an; einen str-Teil hängen Sie unverändert an.
html.escape maskiert <, > und & sowie standardmäßig beide Arten von Anführungszeichen.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import html
from string.templatelib import Interpolation, Template, convert
def safe_html(template: Template) -> str:
"""Build HTML from a t-string, escaping every interpolated value."""
if not isinstance(template, Template):
raise TypeError("safe_html needs a t-string, not a str")
out: list[str] = []
for part in template:
if isinstance(part, Interpolation):
text = format(convert(part.value, part.conversion), part.format_spec)
out.append(html.escape(text))
else:
out.append(part)
return "".join(out)
Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.py
import html
from string.templatelib import Interpolation, Template, convert
def safe_html(template: Template) -> str:
"""Build HTML from a t-string, escaping every interpolated value."""
# This escapes the static markup too, ignores the format specs,
# and quietly accepts a plain str.
text = "".join(str(part.value) if isinstance(part, Interpolation) else part for part in template)
return html.escape(text)
test_main.py
from main import safe_html
def test_escapes_values():
"""Markup in einem Wert wird maskiert"""
name = "<b>Ada</b>"
got = safe_html(t"<p>{name}</p>")
assert got == "<p><b>Ada</b></p>", f"safe_html lieferte {got!r}"
def test_keeps_static_markup():
"""Das Markup im t-String selbst bleibt Markup"""
city = "Bonn"
got = safe_html(t'<a href="/map">{city}</a>')
assert got == '<a href="/map">Bonn</a>', f"safe_html lieferte {got!r}: nur die Interpolationen maskieren"
def test_quotes_escaped():
"""Auch Anführungszeichen in einem Wert werden maskiert"""
title = 'say "hi"'
got = safe_html(t"<b>{title}</b>")
assert got == "<b>say "hi"</b>", f"safe_html lieferte {got!r}"
def test_format_spec():
"""Die Formatangabe wird vor dem Maskieren angewendet"""
total = 1234.5
got = safe_html(t"<td>{total:,.2f}</td>")
assert got == "<td>1,234.50</td>", f"safe_html lieferte {got!r}"
def test_rejects_str():
"""Ein einfacher str (ein fertiger f-String) löst TypeError aus"""
name = "<b>Ada</b>"
try:
safe_html(f"<p>{name}</p>")
except TypeError:
pass
else:
assert False, "safe_html hat einen str angenommen; lösen Sie TypeError aus, wenn es kein Template ist"
Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.
Programm ausführen:
python main.pyPrüfungen ausführen (learnrun.py muss im selben Ordner liegen):
python learnrun.py testlearnrun.py herunterladenname = "Ada"
greeting = t"Hi {name}" + "!"
Was Python ausgibt
TypeError: can only concatenate string.templatelib.Template (not "str") to string.templatelib.TemplateWarum, und die Lösung
Python kann nicht wissen, ob "!" statischer Text oder ein Wert sein soll, daher wird Template + str abgelehnt. Schreiben Sie den Text in den t-String, t"Hi {name}!", oder verpacken Sie ihn: + Template("!") für statischen Text, + Template(Interpolation(value, "name")) für einen Wert.
name = "Ada"
print(t"Hi {name}".upper())
Was Python ausgibt
AttributeError: 'string.templatelib.Template' object has no attribute 'upper'Warum, und die Lösung
Ein Template hat keine String-Methoden, denn es ist noch kein Text. Rendern Sie es zuerst mit Ihrem eigenen Renderer und rufen Sie str-Methoden auf dem Ergebnis auf, oder ändern Sie die Werte, bevor sie in den t-String kommen.
name = "ada"
tpl = t"{name:upper}"
part = tpl.interpolations[0]
print(format(part.value, part.format_spec))
Was Python ausgibt
ValueError: Invalid format specifier 'upper' for object of type 'str'Warum, und die Lösung
Ein t-String nimmt nach dem Doppelpunkt beliebigen Text an, denn der Renderer entscheidet, was er bedeutet. format() versteht nur die Standard-Minisprache. Erfindet Ihr Renderer eigene Angaben wie upper, muss er sie selbst behandeln, bevor er auf format() zurückgreift.
Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.