Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 498 – Interpolation von Zeichenkettenliteralen

Autor:
Eric V. Smith <eric at trueblade.com>
Status:
Final
Typ:
Standards Track
Erstellt:
01-Aug-2015
Python-Version:
3.6
Post-History:
07-Aug-2015, 30-Aug-2015, 04-Sep-2015, 19-Sep-2015, 06-Nov-2016
Resolution:
Python-Dev Nachricht

Inhaltsverzeichnis

Zusammenfassung

Python unterstützt mehrere Möglichkeiten zur Formatierung von Textzeichenketten. Dazu gehören %-Formatierung [1], str.format() [2] und string.Template [3]. Jede dieser Methoden hat ihre Vorteile, aber zusätzlich auch Nachteile, die ihre praktische Anwendung umständlich machen. Dieses PEP schlug einen neuen Mechanismus zur Zeichenkettenformatierung vor: Interpolation von Zeichenkettenliteralen. In diesem PEP werden solche Zeichenketten als „f-Strings“ bezeichnet, abgeleitet vom führenden Zeichen, das zur Kennzeichnung solcher Zeichenketten verwendet wird, und stehend für „formatierte Zeichenketten“.

Dieses PEP schlägt nicht vor, bestehende Mechanismen zur Zeichenkettenformatierung zu entfernen oder zu deprecaten.

F-Strings bieten eine Möglichkeit, Ausdrücke mit minimaler Syntax in Zeichenkettenliterale einzubetten. Es sei darauf hingewiesen, dass ein f-String eigentlich ein zur Laufzeit ausgewerteter Ausdruck und kein konstanter Wert ist. Im Python-Quellcode ist ein f-String ein Zeichenkettenliteral, dem ein ‚f‘ vorangestellt ist und das Ausdrücke in geschweiften Klammern enthält. Die Ausdrücke werden durch ihre Werte ersetzt. Einige Beispiele sind

>>> import datetime
>>> name = 'Fred'
>>> age = 50
>>> anniversary = datetime.date(1991, 10, 12)
>>> f'My name is {name}, my age next year is {age+1}, my anniversary is {anniversary:%A, %B %d, %Y}.'
'My name is Fred, my age next year is 51, my anniversary is Saturday, October 12, 1991.'
>>> f'He said his name is {name!r}.'
"He said his name is 'Fred'."

Eine ähnliche Funktion wurde in PEP 215 vorgeschlagen. PEP 215 schlug die Unterstützung einer Teilmenge von Python-Ausdrücken vor und unterstützte nicht die typenspezifische Zeichenkettenformatierung (die __format__()-Methode), die mit PEP 3101 eingeführt wurde.

Begründung

Dieses PEP wird durch den Wunsch angetrieben, eine einfachere Methode zur Zeichenkettenformatierung in Python zu haben. Die bestehenden Formatierungsmethoden sind entweder fehleranfällig, unflexibel oder umständlich.

Die %-Formatierung ist in Bezug auf die unterstützten Typen eingeschränkt. Nur Ganzzahlen, Zeichenketten und Gleitkommazahlen können formatiert werden. Alle anderen Typen werden entweder nicht unterstützt oder vor der Formatierung in einen dieser Typen umgewandelt. Zusätzlich gibt es eine bekannte Falle, bei der ein einzelner Wert übergeben wird

>>> msg = 'disk failure'
>>> 'error: %s' % msg
'error: disk failure'

Aber wenn msg jemals ein Tupel wäre, würde derselbe Code fehlschlagen

>>> msg = ('disk failure', 32)
>>> 'error: %s' % msg
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting

Um sicherzustellen, dass der folgende Code funktioniert, sollte er verwendet werden

>>> 'error: %s' % (msg,)
"error: ('disk failure', 32)"

str.format() wurde hinzugefügt, um einige dieser Probleme mit der %-Formatierung zu beheben. Insbesondere verwendet es die normale Funktionsaufrufsyntax (und unterstützt daher mehrere Parameter) und ist durch die __format__()-Methode des zu einer Zeichenkette zu konvertierenden Objekts erweiterbar. Siehe PEP 3101 für eine detaillierte Begründung. Dieses PEP greift einen Großteil der Syntax und des Mechanismus von str.format() wieder auf, um Kontinuität mit einem bestehenden Python-Mechanismus zur Zeichenkettenformatierung zu gewährleisten.

Jedoch ist str.format() nicht ohne seine Probleme. Die Hauptprobleme sind seine Ausführlichkeit. Zum Beispiel wird der Text value hier wiederholt

>>> value = 4 * 20
>>> 'The value is {value}.'.format(value=value)
'The value is 80.'

Selbst in seiner einfachsten Form gibt es etwas Boilerplate, und der Wert, der in den Platzhalter eingefügt wird, ist manchmal weit von dem Ort entfernt, an dem der Platzhalter sich befindet

>>> 'The value is {}.'.format(value)
'The value is 80.'

Mit einem f-String wird dies zu

>>> f'The value is {value}.'
'The value is 80.'

F-Strings bieten eine prägnante, lesbare Möglichkeit, den Wert von Python-Ausdrücken in Zeichenketten einzuschließen.

In diesem Sinne haben string.Template und %-Formatierung ähnliche Mängel wie str.format(), unterstützen aber auch weniger Formatierungsoptionen. Insbesondere unterstützen sie nicht das __format__-Protokoll, sodass es keine Möglichkeit gibt zu steuern, wie ein bestimmtes Objekt in eine Zeichenkette konvertiert wird, noch kann es auf zusätzliche Typen erweitert werden, die steuern möchten, wie sie in Zeichenketten konvertiert werden (wie Decimal und datetime). Dieses Beispiel ist mit string.Template nicht möglich

>>> value = 1234
>>> f'input={value:#06x}'
'input=0x04d2'

Und weder %-Formatierung noch string.Template können Formatierungen wie diese steuern

>>> date = datetime.date(1991, 10, 12)
>>> f'{date} was on a {date:%A}'
'1991-10-12 was on a Saturday'

Keine Verwendung von globals() oder locals()

In den Diskussionen auf python-dev [4] wurden eine Reihe von Lösungen präsentiert, die locals() und globals() oder deren Äquivalente verwendeten. All diese haben verschiedene Probleme. Zu diesen gehören das Referenzieren von Variablen, die ansonsten nicht in einem Closure verwendet werden. Betrachten Sie

>>> def outer(x):
...     def inner():
...         return 'x={x}'.format_map(locals())
...     return inner
...
>>> outer(42)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in inner
KeyError: 'x'

Dies gibt einen Fehler zurück, da der Compiler keine Referenz auf x innerhalb des Closures hinzugefügt hat. Sie müssen manuell eine Referenz auf x hinzufügen, damit dies funktioniert

>>> def outer(x):
...     def inner():
...         x
...         return 'x={x}'.format_map(locals())
...     return inner
...
>>> outer(42)()
'x=42'

Darüber hinaus führt die Verwendung von locals() oder globals() zu einem Informationsleck. Eine aufgerufene Routine, die Zugriff auf die locals() oder globals() des Aufrufers hat, hat Zugriff auf weitaus mehr Informationen als zur String-Interpolation benötigt werden.

Guido erklärte [5], dass jede Lösung für eine bessere String-Interpolation locals() oder globals() nicht in ihrer Implementierung verwenden würde. (Dies verbietet Benutzern nicht, locals() oder globals() zu übergeben, es erfordert es nur nicht und erlaubt auch nicht die Verwendung dieser Funktionen im Hintergrund.)

Spezifikation

Im Quellcode sind f-Strings Zeichenkettenliterale, denen der Buchstabe ‚f‘ oder ‚F‘ vorangestellt ist. Überall, wo dieses PEP ‚f‘ verwendet, kann auch ‚F‘ verwendet werden. ‚f‘ kann mit ‚r‘ oder ‚R‘ kombiniert werden, in beliebiger Reihenfolge, um Raw-f-String-Literale zu erzeugen. ‚f‘ darf nicht mit ‚b‘ kombiniert werden: Dieses PEP schlägt keine binären f-Strings vor. ‚f‘ darf nicht mit ‚u‘ kombiniert werden.

Beim Tokenisieren von Quellcodedateien verwenden f-Strings dieselben Regeln wie normale Zeichenketten, Raw-Zeichenketten, Binärzeichenketten und dreifach-zitierte Zeichenketten. Das heißt, die Zeichenkette muss mit demselben Zeichen enden, mit dem sie begonnen hat: Wenn sie mit einem einfachen Anführungszeichen beginnt, muss sie mit einem einfachen Anführungszeichen enden usw. Dies impliziert, dass jeder Code, der derzeit Python-Code auf Zeichenketten durchsucht, trivial modifizierbar sein sollte, um f-Strings zu erkennen (das Parsen innerhalb eines f-Strings ist natürlich eine andere Sache).

Nach dem Tokenisieren werden f-Strings in Zeichenkettenliterale und Ausdrücke zerlegt. Ausdrücke erscheinen innerhalb geschweifter Klammern '{' und '}'. Während die Zeichenkette nach Ausdrücken durchsucht wird, werden alle doppelten Klammern '{{' oder '}}' innerhalb von literalen Teilen eines f-Strings durch die entsprechende einzelne Klammer ersetzt. Doppelte literale öffnende Klammern bedeuten nicht den Beginn eines Ausdrucks. Eine einzelne schließende geschweifte Klammer '}' im literalen Teil einer Zeichenkette ist ein Fehler: literale schließende geschweifte Klammern müssen verdoppelt werden '}}', um eine einzelne schließende Klammer darzustellen.

Die Teile des f-Strings außerhalb der Klammern sind literale Zeichenketten. Diese literalen Teile werden dann dekodiert. Für Nicht-Raw-f-Strings beinhaltet dies die Umwandlung von Backslash-Escapes wie '\n', '\"', "\'", '\xhh', '\uxxxx', '\Uxxxxxxxx' und benannte Unicode-Zeichen '\N{name}' in ihre entsprechenden Unicode-Zeichen [6].

Backslashes dürfen nirgendwo innerhalb von Ausdrücken vorkommen. Kommentare, die das Zeichen '#' verwenden, sind in einem Ausdruck nicht erlaubt.

Nach jedem Ausdruck kann eine optionale Typkonvertierung angegeben werden. Die zulässigen Konvertierungen sind '!s', '!r' oder '!a'. Diese werden genauso behandelt wie in str.format(): '!s' ruft str() für den Ausdruck auf, '!r' ruft repr() für den Ausdruck auf und '!a' ruft ascii() für den Ausdruck auf. Diese Konvertierungen werden angewendet, bevor format() aufgerufen wird. Der einzige Grund, '!s' zu verwenden, ist, wenn Sie einen Format-Spezifizierer angeben möchten, der für str gilt und nicht für den Typ des Ausdrucks.

F-Strings verwenden dieselbe Mini-Sprache für Format-Spezifizierer wie str.format. Ähnlich wie bei str.format() können optionale Format-Spezifizierer innerhalb des f-Strings enthalten sein, getrennt vom Ausdruck (oder der angegebenen Typkonvertierung) durch einen Doppelpunkt. Wenn kein Format-Spezifizierer bereitgestellt wird, wird eine leere Zeichenkette verwendet.

Daher sieht ein f-String so aus

f ' <text> { <expression> <optional !s, !r, or !a> <optional : format specifier> } <text> ... '

Der Ausdruck wird dann mithilfe des __format__-Protokolls mit dem Format-Spezifizierer als Argument formatiert. Der resultierende Wert wird beim Erstellen des f-String-Werts verwendet.

Beachten Sie, dass __format__() nicht direkt für jeden Wert aufgerufen wird. Der tatsächliche Code verwendet das Äquivalent von type(value).__format__(value, format_spec) oder format(value, format_spec). Weitere Details finden Sie in der Dokumentation der eingebauten Funktion format().

Ausdrücke dürfen keine ':' oder '!' außerhalb von Zeichenketten oder Klammern, eckigen Klammern oder geschweiften Klammern enthalten. Die Ausnahme ist, dass der Operator '!=' als Sonderfall erlaubt ist.

Escape-Sequenzen

Backslashes dürfen nicht innerhalb der Ausdrucksteile von f-Strings vorkommen, sodass Sie sie beispielsweise nicht verwenden können, um Anführungszeichen innerhalb von f-Strings zu maskieren

>>> f'{\'quoted string\'}'
  File "<stdin>", line 1
SyntaxError: f-string expression part cannot include a backslash

Sie können eine andere Art von Anführungszeichen innerhalb des Ausdrucks verwenden

>>> f'{"quoted string"}'
'quoted string'

Backslash-Escapes dürfen innerhalb der Zeichenkettenanteile eines f-Strings vorkommen.

Beachten Sie, dass der richtige Weg, um eine literale geschweifte Klammer im resultierenden Zeichenkettenwert zu erhalten, die Verdoppelung der Klammer ist

>>> f'{{ {4*10} }}'
'{ 40 }'
>>> f'{{{4*10}}}'
'{40}'

Wie bei allen Raw-Strings in Python wird bei Raw-f-Strings keine Escape-Verarbeitung durchgeführt

>>> fr'x={4*10}\n'
'x=40\\n'

Aufgrund der String-Tokenisierungsregeln von Python ist der f-String f'abc {a['x']} def' ungültig. Der Tokenizer zerlegt dies in 3 Token: f'abc {a[', x und ']} def'. Genau wie bei normalen Zeichenketten kann dies nicht durch die Verwendung von Raw-Strings behoben werden. Es gibt eine Reihe von korrekten Möglichkeiten, diesen f-String zu schreiben: mit einem anderen Anführungszeichen

f"abc {a['x']} def"

Oder mit dreifachen Anführungszeichen

f'''abc {a['x']} def'''

Code-Äquivalenz

Der genaue Code zur Implementierung von f-Strings ist nicht spezifiziert. Es wird jedoch garantiert, dass jeder eingebettete Wert, der in eine Zeichenkette konvertiert wird, die __format__-Methode dieses Wertes verwendet. Dies ist derselbe Mechanismus, den str.format() zur Konvertierung von Werten in Zeichenketten verwendet.

Zum Beispiel dieser Code

f'abc{expr1:spec1}{expr2!r:spec2}def{expr3}ghi'

Könnte ausgewertet werden als

'abc' + format(expr1, spec1) + format(repr(expr2), spec2) + 'def' + format(expr3) + 'ghi'

Auswertung von Ausdrücken

Die Ausdrücke, die aus der Zeichenkette extrahiert werden, werden im Kontext ausgewertet, in dem der f-String aufgetreten ist. Das bedeutet, dass der Ausdruck vollen Zugriff auf lokale und globale Variablen hat. Jeder gültige Python-Ausdruck kann verwendet werden, einschließlich Funktions- und Methodenaufrufen.

Da die f-Strings dort ausgewertet werden, wo die Zeichenkette im Quellcode erscheint, gibt es mit f-Strings keine zusätzliche Ausdrucksmöglichkeit. Es gibt auch keine zusätzlichen Sicherheitsbedenken: Sie hätten denselben Ausdruck auch außerhalb eines f-Strings schreiben können.

>>> def foo():
...   return 20
...
>>> f'result={foo()}'
'result=20'

Entspricht

>>> 'result=' + str(foo())
'result=20'

Ausdrücke werden mit dem Äquivalent von ast.parse('(' + expression + ')', '<fstring>', 'eval') geparast [7].

Beachten Sie, dass Ausdrücke Zeilenumbrüche enthalten können, da der Ausdruck vor der Auswertung in implizite Klammern eingeschlossen wird. Zum Beispiel

>>> x = 0
>>> f'''{x
... +1}'''
'1'

>>> d = {0: 'zero'}
>>> f'''{d[0
... ]}'''
'zero'

Format-Spezifizierer

Format-Spezifizierer können ebenfalls ausgewertete Ausdrücke enthalten. Dies ermöglicht Code wie

>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal('12.34567')
>>> f'result: {value:{width}.{precision}}'
'result:      12.35'

Sobald Ausdrücke in einem Format-Spezifizierer ausgewertet wurden (falls erforderlich), werden Format-Spezifizierer nicht vom f-String-Evaluator interpretiert. Genau wie in str.format() werden sie lediglich an die __format__()-Methode des zu formatierenden Objekts übergeben.

Verketten von Zeichenketten

Nebeneinander liegende f-Strings und reguläre Zeichenketten werden verkettet. Reguläre Zeichenketten werden zur Kompilierzeit verkettet und f-Strings zur Laufzeit. Zum Beispiel der Ausdruck

>>> x = 10
>>> y = 'hi'
>>> 'a' 'b' f'{x}' '{c}' f'str<{y:^4}>' 'd' 'e'

ergibt den Wert

'ab10{c}str< hi >de'

Während die genaue Methode dieser Laufzeitverkettung nicht spezifiziert ist, könnte der obige Code ausgewertet werden zu

'ab' + format(x) + '{c}' + 'str<' + format(y, '^4') + '>de'

Jeder f-String wird vollständig ausgewertet, bevor er an benachbarte f-Strings angehängt wird. Das bedeutet, dass dies

>>> f'{x' f'}'

Ein Syntaxfehler ist, da der erste f-String keine schließende Klammer enthält.

Fehlerbehandlung

Beim Verarbeiten von f-Strings können Fehler zur Kompilierzeit oder zur Laufzeit auftreten. Kompilierzeitfehler beschränken sich auf Fehler, die beim Scannen eines f-Strings erkannt werden können. Diese Fehler lösen alle SyntaxError aus.

Nicht übereinstimmende Klammern

>>> f'x={x'
  File "<stdin>", line 1
SyntaxError: f-string: expecting '}'

Ungültige Ausdrücke

>>> f'x={!x}'
  File "<stdin>", line 1
SyntaxError: f-string: empty expression not allowed

Laufzeitfehler treten bei der Auswertung der Ausdrücke innerhalb eines f-Strings auf. Beachten Sie, dass ein f-String mehrmals ausgewertet werden kann und manchmal funktioniert und ein anderes Mal einen Fehler auslöst

>>> d = {0:10, 1:20}
>>> for i in range(3):
...     print(f'{i}:{d[i]}')
...
0:10
1:20
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyError: 2

oder

>>> for x in (32, 100, 'fifty'):
...   print(f'x = {x:+3}')
...
'x = +32'
'x = +100'
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: Sign not allowed in string format specifier

Führende und abschließende Leerzeichen in Ausdrücken werden ignoriert

Zur besseren Lesbarkeit werden führende und abschließende Leerzeichen in Ausdrücken ignoriert. Dies ist ein Nebeneffekt des Einschließens des Ausdrucks in Klammern vor der Auswertung.

Auswertungsreihenfolge von Ausdrücken

Die Ausdrücke in einem f-String werden von links nach rechts ausgewertet. Dies ist nur erkennbar, wenn die Ausdrücke Seiteneffekte haben

>>> def fn(l, incr):
...    result = l[0]
...    l[0] += incr
...    return result
...
>>> lst = [0]
>>> f'{fn(lst,2)} {fn(lst,3)}'
'0 2'
>>> f'{fn(lst,2)} {fn(lst,3)}'
'5 7'
>>> lst
[10]

Diskussion

Diskussion zu python-ideas

Die meisten Diskussionen auf python-ideas [8] konzentrierten sich auf drei Punkte

  • Wie f-Strings gekennzeichnet werden,
  • Wie die Position von Ausdrücken in f-Strings spezifiziert wird und
  • Ob volle Python-Ausdrücke erlaubt werden sollen.

Wie f-Strings gekennzeichnet werden

Da der Compiler an der Auswertung der in den interpolierten Zeichenketten enthaltenen Ausdrücke beteiligt sein muss, muss es eine Möglichkeit geben, dem Compiler anzuzeigen, welche Zeichenketten ausgewertet werden sollen. Dieses PEP wählte ein führendes ‚f‘-Zeichen vor dem Zeichenkettenliteral. Dies ähnelt der Art und Weise, wie ‚b‘- und ‚r‘-Präfixe die Bedeutung der Zeichenkette selbst zur Kompilierzeit ändern. Andere Präfixe wurden vorgeschlagen, wie z. B. ‚i‘. Keine Option schien besser als die andere, daher wurde ‚f‘ gewählt.

Eine andere Option war die Unterstützung spezieller Funktionen, die dem Compiler bekannt sind, wie z. B. Format(). Dies erscheint zu magisch für Python: Nicht nur besteht die Gefahr einer Kollision mit vorhandenen Bezeichnern, der PEP-Autor ist der Meinung, dass es besser ist, die Magie mit einem Zeichenkettenpräfix zu kennzeichnen.

Wie die Position von Ausdrücken in f-Strings spezifiziert wird

Dieses PEP unterstützt dieselbe Syntax wie str.format() zur Unterscheidung von Ersatztexten innerhalb von Zeichenketten: Ausdrücke werden in geschweiften Klammern gehalten. Es wurden andere Optionen vorgeschlagen, wie z. B. string.Templates $identifier oder ${expression}.

Während $identifier zweifellos für Shell-Skript-Benutzer und Benutzer einiger anderer Sprachen bekannter ist, wird in Python str.format() intensiv genutzt. Eine schnelle Suche in Pythons Standardbibliothek zeigt nur eine Handvoll Verwendungen von string.Template, aber Hunderte von Verwendungen von str.format().

Eine weitere vorgeschlagene Alternative war, den substituierten Text zwischen \{ und } oder zwischen \{ und \} zu haben. Während diese Syntax wahrscheinlich wünschenswert wäre, wenn alle Zeichenkettenliterale Interpolation unterstützen würden, unterstützt dieses PEP nur Zeichenketten, die bereits mit dem führenden 'f' markiert sind. Daher verwendet das PEP ungekennzeichnete Klammern zur Kennzeichnung von substituiertem Text, um die Vertrautheit der Endbenutzer mit str.format() zu nutzen.

Unterstützung vollständiger Python-Ausdrücke

Viele Leute in der python-ideas-Diskussion wünschten sich Unterstützung für entweder nur einzelne Bezeichner oder eine begrenzte Teilmenge von Python-Ausdrücken (wie die von str.format() unterstützte Teilmenge). Dieses PEP unterstützt vollständige Python-Ausdrücke innerhalb der Klammern. Ohne vollständige Ausdrücke wäre einige wünschenswerte Verwendung umständlich. Zum Beispiel

>>> f'Column={col_idx+1}'
>>> f'number of items: {len(items)}'

würde zu

>>> col_number = col_idx+1
>>> f'Column={col_number}'
>>> n_items = len(items)
>>> f'number of items: {n_items}'

werden. Obwohl es stimmt, dass sehr hässliche Ausdrücke in die f-Strings aufgenommen werden könnten, nimmt dieses PEP die Position ein, dass solche Verwendungen in einem Linter oder einer Code-Überprüfung behandelt werden sollten.

>>> f'mapping is { {a:b for (a, b) in ((1, 2), (3, 4))} }'
'mapping is {1: 2, 3: 4}'

Ähnliche Unterstützung in anderen Sprachen

Wikipedia hat eine gute Diskussion über String-Interpolation in anderen Programmiersprachen [9]. Dieses Feature ist in vielen Sprachen mit einer Vielzahl von Syntaxen und Einschränkungen implementiert.

Unterschiede zwischen f-String- und str.format-Ausdrücken

Es gibt einen kleinen Unterschied zwischen den eingeschränkten Ausdrücken, die in str.format() erlaubt sind, und den vollen Ausdrücken, die in f-Strings erlaubt sind. Der Unterschied liegt darin, wie Index-Lookups durchgeführt werden. In str.format() werden Indexwerte, die nicht wie Zahlen aussehen, in Zeichenketten konvertiert

>>> d = {'a': 10, 'b': 20}
>>> 'a={d[a]}'.format(d=d)
'a=10'

Beachten Sie, dass der Indexwert in den String 'a' konvertiert wird, wenn er im Dict nachgeschlagen wird.

In f-Strings müssten Sie jedoch ein Literal für den Wert von 'a' verwenden

>>> f'a={d["a"]}'
'a=10'

Dieser Unterschied ist erforderlich, da Sie sonst keine Variablen als Indexwerte verwenden könnten

>>> a = 'b'
>>> f'a={d[a]}'
'a=20'

Siehe [10] für eine weitere Diskussion. Diese Beobachtung führte zur Unterstützung vollständiger Python-Ausdrücke in f-Strings.

Darüber hinaus müssen die eingeschränkten Ausdrücke, die str.format() versteht, keine gültigen Python-Ausdrücke sein. Zum Beispiel

>>> '{i[";]}'.format(i={'";':4})
'4'

Aus diesem Grund ist der „Ausdrucks-Parser“ von str.format() nicht für die Implementierung von f-Strings geeignet.

Dreifach-zitierte f-Strings

Dreifach-zitierte f-Strings sind erlaubt. Diese Zeichenketten werden genauso geparst wie normale dreifach-zitierte Zeichenketten. Nach dem Parsen und Dekodieren wird die normale f-String-Logik angewendet und __format__() für jeden Wert aufgerufen.

Raw f-Strings

Raw- und f-Strings können kombiniert werden. Zum Beispiel können sie verwendet werden, um reguläre Ausdrücke zu erstellen

>>> header = 'Subject'
>>> fr'{header}:\s+'
'Subject:\\s+'

Zusätzlich können Raw-f-Strings mit dreifach-zitierten Zeichenketten kombiniert werden.

Keine binären f-Strings

Aus demselben Grund, aus dem wir bytes.format() nicht unterstützen, dürfen Sie ‚f‘ nicht mit ‚b‘-Zeichenkettenliteralen kombinieren. Das Hauptproblem ist, dass die __format__()-Methode eines Objekts Unicode-Daten zurückgeben kann, die nicht mit einer Bytes-Zeichenkette kompatibel sind.

Binäre f-Strings würden zunächst eine Lösung für bytes.format() erfordern. Diese Idee wurde in der Vergangenheit vorgeschlagen, zuletzt in PEP 461. Die Diskussionen über eine solche Funktion schlagen normalerweise entweder

  • das Hinzufügen einer Methode wie __bformat__(), damit ein Objekt steuern kann, wie es in Bytes konvertiert wird, oder
  • die Tatsache, dass bytes.format() nicht so allgemein oder erweiterbar ist wie str.format().

Beides bleibt als Option für die Zukunft, falls eine solche Funktionalität gewünscht wird.

!s, !r und !a sind redundant

Die Konvertierungen !s, !r und !a sind nicht streng erforderlich. Da beliebige Ausdrücke innerhalb der f-Strings erlaubt sind, ist dieser Code

>>> a = 'some string'
>>> f'{a!r}'
"'some string'"

Identisch mit

>>> f'{repr(a)}'
"'some string'"

Ebenso kann !s durch Aufrufe von str() und !a durch Aufrufe von ascii() ersetzt werden.

Jedoch werden !s, !r und !a von diesem PEP unterstützt, um die Unterschiede zu str.format() zu minimieren. !s, !r und !a sind in str.format() erforderlich, da diese die Ausführung beliebiger Ausdrücke nicht zulässt.

Lambdas innerhalb von Ausdrücken

Da Lambdas das Zeichen ':' verwenden, können sie außerhalb von Klammern in einem Ausdruck nicht vorkommen. Der Doppelpunkt wird als Beginn des Format-Spezifizierers interpretiert, was bedeutet, dass der Beginn des Lambda-Ausdrucks gesehen wird und syntaktisch ungültig ist. Da es keinen praktischen Nutzen für ein einfaches Lambda in einem f-String-Ausdruck gibt, wird dies nicht als große Einschränkung angesehen.

Wenn Sie der Meinung sind, dass Sie Lambdas verwenden müssen, können sie innerhalb von Klammern verwendet werden

>>> f'{(lambda x: x*2)(3)}'
'6'

Kann nicht mit „u“ kombiniert werden

Das ‚u‘-Präfix wurde in Python 3.3 in PEP 414 als Mittel zur Erleichterung der Quellcode-Kompatibilität mit Python 2.7 hinzugefügt. Da Python 2.7 niemals f-Strings unterstützen wird, gibt es keinen Vorteil daraus, das ‚f‘-Präfix mit ‚u‘ kombinieren zu können.

Beispiele aus Pythons Quellcode

Hier sind einige Beispiele aus dem Python-Quellcode, die derzeit str.format() verwenden, und wie sie mit f-Strings aussehen würden. Dieses PEP empfiehlt keine pauschale Umwandlung zu f-Strings, dies sind nur Beispiele für reale Verwendungen von str.format() und wie sie aussehen würden, wenn sie von Grund auf neu mit f-Strings geschrieben würden.

Lib/asyncio/locks.py:

extra = '{},waiters:{}'.format(extra, len(self._waiters))
extra = f'{extra},waiters:{len(self._waiters)}'

Lib/configparser.py:

message.append(" [line {0:2d}]".format(lineno))
message.append(f" [line {lineno:2d}]")

Tools/clinic/clinic.py:

methoddef_name = "{}_METHODDEF".format(c_basename.upper())
methoddef_name = f"{c_basename.upper()}_METHODDEF"

python-config.py:

print("Usage: {0} [{1}]".format(sys.argv[0], '|'.join('--'+opt for opt in valid_opts)), file=sys.stderr)
print(f"Usage: {sys.argv[0]} [{'|'.join('--'+opt for opt in valid_opts)}]", file=sys.stderr)

Referenzen


Quelle: https://github.com/python/peps/blob/main/peps/pep-0498.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT