PEP 3101 – Erweiterte Zeichenkettenformatierung
- Autor:
- Talin <viridia at gmail.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 16. April 2006
- Python-Version:
- 3.0
- Post-History:
- 28. April 2006, 06. Mai 2006, 10. Juni 2007, 14. August 2007, 14. September 2008
Zusammenfassung
Dieses PEP schlägt ein neues System für integrierte Zeichenkettenformatierungsoperationen vor, das als Ersatz für den bestehenden Formatierungsoperator für Zeichenketten mit '%' dienen soll.
Begründung
Python bietet derzeit zwei Methoden der Zeichenketteninterpolation
Der primäre Fokus dieses PEP betrifft Vorschläge für integrierte Zeichenkettenformatierungsoperationen (mit anderen Worten, Methoden des integrierten Zeichentyps).
Der '%' Operator ist hauptsächlich durch die Tatsache eingeschränkt, dass er ein binärer Operator ist und daher höchstens zwei Argumente annehmen kann. Eines dieser Argumente ist bereits für die Formatzeichenkette reserviert, sodass alle anderen Variablen in das verbleibende Argument gequetscht werden müssen. Die aktuelle Praxis besteht darin, entweder ein Wörterbuch oder ein Tupel als zweites Argument zu verwenden, aber wie viele Leute kommentiert haben [3], fehlt hier Flexibilität. Der "Alles oder Nichts"-Ansatz (was bedeutet, dass man sich entweder nur zwischen positionsbezogenen Argumenten oder nur benannten Argumenten entscheiden muss) wird als übermäßig einschränkend empfunden.
Obwohl es eine gewisse Überschneidung zwischen diesem Vorschlag und string.Template gibt, wird davon ausgegangen, dass jeder einen eigenen Bedarf erfüllt und keiner den anderen überflüssig macht. Dieser Vorschlag betrifft einen Mechanismus, der, wie '%', effizient für kleine Zeichenketten ist, die nur einmal verwendet werden. Daher ist beispielsweise die Kompilierung einer Zeichenkette in eine Vorlage in diesem Vorschlag nicht vorgesehen, obwohl der Vorschlag darauf abzielt, Formatzeichenketten und die API so zu definieren, dass ein effizientes Vorlagenpaket die Syntax und sogar einen Teil des zugrundeliegenden Formatierungscodes wiederverwenden könnte.
Spezifikation
Die Spezifikation wird aus folgenden Teilen bestehen:
- Spezifikation einer neuen Formatierungsmethode, die zur integrierten Zeichenkettenklasse hinzugefügt werden soll.
- Spezifikation von Funktionen und Flag-Werten, die dem string-Modul hinzugefügt werden sollen, damit die zugrundeliegende Formatierungs-Engine mit zusätzlichen Optionen verwendet werden kann.
- Spezifikation einer neuen Syntax für Formatzeichenketten.
- Spezifikation eines neuen Satzes von Sonder-Methoden zur Steuerung der Formatierung und Konvertierung von Objekten.
- Spezifikation einer API für benutzerdefinierte Formatierungsklassen.
- Spezifikation, wie Formatierungsfehler behandelt werden.
Hinweis zu Zeichenkettenkodierungen: Bei der Diskussion dieses PEP im Kontext von Python 3.0 wird davon ausgegangen, dass alle Zeichenketten Unicode-Zeichenketten sind und dass die Verwendung des Wortes 'Zeichenkette' im Kontext dieses Dokuments im Allgemeinen eine Python 3.0-Zeichenkette bezeichnet, die dasselbe ist wie ein Python 2.x Unicode-Objekt.
Im Kontext von Python 2.x bezieht sich die Verwendung des Wortes 'Zeichenkette' in diesem Dokument auf ein Objekt, das entweder eine reguläre Zeichenkette oder ein Unicode-Objekt sein kann. Alle in diesem PEP beschriebenen Funktionsaufrufschnittstellen können sowohl für Zeichenketten als auch für Unicode-Objekte verwendet werden, und in allen Fällen sind genügend Informationen vorhanden, um den Ausgabezwischenketten-Typ korrekt abzuleiten (mit anderen Worten, es sind keine zwei separaten APIs erforderlich). In allen Fällen dominiert der Typ der Formatzeichenkette - das heißt, das Ergebnis der Konvertierung führt immer zu einem Objekt, das dieselbe Darstellung von Zeichen wie die Eingabe-Formatzeichenkette enthält.
Zeichenkettenmethoden
Die integrierte Zeichenkettenklasse (und auch die Unicode-Klasse in 2.6) erhält eine neue Methode, 'format', die eine beliebige Anzahl von positionsbezogenen und Schlüsselwortargumenten entgegennimmt.
"The story of {0}, {1}, and {c}".format(a, b, c=d)
Innerhalb einer Formatzeichenkette wird jedes positionsbezogene Argument mit einer Zahl identifiziert, beginnend bei Null. Im obigen Beispiel ist 'a' Argument 0 und 'b' ist Argument 1. Jedes Schlüsselwortargument wird durch seinen Schlüsselwortnamen identifiziert. Im obigen Beispiel wird 'c' verwendet, um auf das dritte Argument zu verweisen.
Es gibt auch eine globale integrierte Funktion, 'format', die einen einzelnen Wert formatiert.
print(format(10.0, "7.3g"))
Diese Funktion wird in einem späteren Abschnitt beschrieben.
Formatzeichenketten
Formatzeichenketten bestehen aus vermischten Zeichendaten und Markups.
Zeichendaten sind Daten, die unverändert von der Formatzeichenkette in die Ausgabezwischenkette übertragen werden; Markups werden nicht direkt von der Formatzeichenkette in die Ausgabe übertragen, sondern stattdessen zur Definition von 'Ersetzungsfeldern' verwendet, die der Formatierungs-Engine mitteilen, was anstelle des Markups in die Ausgabezwischenkette platziert werden soll.
Klammern ('geschweifte Klammern') werden verwendet, um ein Ersetzungsfeld innerhalb der Zeichenkette anzuzeigen.
"My name is {0}".format('Fred')
Das Ergebnis ist die Zeichenkette
"My name is Fred"
Klammern können durch Verdopplung maskiert werden.
"My name is {0} :-{{}}".format('Fred')
Was folgendes produzieren würde:
"My name is Fred :-{}"
Das Element innerhalb der Klammern wird als 'Feld' bezeichnet. Felder bestehen aus einem 'Feldnamen', der entweder einfach oder zusammengesetzt sein kann, und einem optionalen 'Format-Spezifizierer'.
Einfache und zusammengesetzte Feldnamen
Einfache Feldnamen sind entweder Namen oder Zahlen. Wenn es sich um Zahlen handelt, müssen sie gültige Dezimalzahlen sein; wenn es sich um Namen handelt, müssen sie gültige Python-Identifikatoren sein. Eine Zahl wird verwendet, um ein positionsbezogenes Argument zu identifizieren, während ein Name zur Identifizierung eines Schlüsselwortarguments verwendet wird.
Ein zusammengesetzter Feldname ist eine Kombination aus mehreren einfachen Feldnamen in einem Ausdruck.
"My name is {0.name}".format(open('out.txt', 'w'))
Dieses Beispiel zeigt die Verwendung des 'getattr'- oder 'Punkt'-Operators in einem Feldausdruck. Der Punkt-Operator ermöglicht die Angabe eines Attributs eines Eingabewerts als Feldwert.
Im Gegensatz zu anderen Programmiersprachen können Sie keine beliebigen Ausdrücke in Formatzeichenketten einbetten. Dies ist beabsichtigt - die Arten von Ausdrücken, die Sie verwenden können, sind bewusst begrenzt. Es werden nur zwei Operatoren unterstützt: der '.' (getattr)-Operator und der '[]' (getitem)-Operator. Der Grund für die Zulassung dieser Operatoren ist, dass sie in nicht-pathologischem Code normalerweise keine Nebeneffekte haben.
Ein Beispiel für die 'getitem'-Syntax
"My name is {0[name]}".format(dict(name='Fred'))
Es ist zu beachten, dass die Verwendung von 'getitem' innerhalb einer Formatzeichenkette viel stärker eingeschränkt ist als ihre übliche Verwendung. Im obigen Beispiel ist die Zeichenkette 'name' tatsächlich die literale Zeichenkette 'name', nicht eine Variable namens 'name'. Die Regeln für das Parsen eines Elementschlüssels sind sehr einfach. Wenn er mit einer Ziffer beginnt, wird er als Zahl behandelt, andernfalls wird er als Zeichenkette verwendet.
Da Schlüssel nicht durch Anführungszeichen begrenzt sind, ist es nicht möglich, beliebige Wörterbuchschlüssel (z. B. die Zeichenketten "10" oder ":-]") innerhalb einer Formatzeichenkette anzugeben.
Implementierungshinweis: Die Implementierung dieses Vorschlags muss die Regel, dass ein einfacher oder gepunkteter Name ein gültiger Python-Identifikator ist, nicht erzwingen. Stattdessen wird die getattr-Funktion des zugrundeliegenden Objekts verwendet, um eine Ausnahme auszulösen, wenn der Identifikator nicht legal ist. Die Funktion str.format() wird einen minimalistischen Parser haben, der nur versucht herauszufinden, wann er mit einem Bezeichner "fertig" ist (indem er einen '.', ']' oder '}' usw. findet).
Format-Spezifizierer
Jedes Feld kann auch eine optionale Gruppe von 'Format-Spezifizierern' angeben, die verwendet werden können, um das Format dieses Feldes anzupassen. Format-Spezifizierer folgen dem Feldnamen, wobei ein Doppelpunkt (':') die beiden trennt.
"My name is {0:8}".format('Fred')
Die Bedeutung und Syntax der Format-Spezifizierer hängt vom Typ des zu formatierenden Objekts ab, aber es gibt eine Standardmenge von Format-Spezifizierern, die für jedes Objekt verwendet werden, das diese nicht überschreibt.
Format-Spezifizierer können selbst Ersetzungsfelder enthalten. Zum Beispiel könnte ein Feld, dessen Feldbreite selbst ein Parameter ist, spezifiziert werden über
"{0:{1}}".format(a, b)
Diese 'internen' Ersetzungsfelder können nur im Format-Spezifiziererteil des Ersetzungsfeldes vorkommen. Interne Ersetzungsfelder können selbst keine Format-Spezifizierer haben. Dies impliziert auch, dass Ersetzungsfelder nicht bis zu beliebigen Ebenen verschachtelt werden können.
Beachten Sie, dass die verdoppelten '}' am Ende, die normalerweise maskiert würden, in diesem Fall nicht maskiert sind. Der Grund dafür ist, dass die Syntax '{{' und '}}' für Maskierungen nur verwendet wird, **außerhalb** eines Formatfeldes. Innerhalb eines Formatfeldes haben die Klammern immer ihre normale Bedeutung.
Die Syntax für Format-Spezifizierer ist offen, da eine Klasse die Standard-Format-Spezifizierer überschreiben kann. In solchen Fällen gibt die Methode str.format() lediglich alle Zeichen zwischen dem ersten Doppelpunkt und der übereinstimmenden Klammer an die entsprechende zugrundeliegende Formatierungsmethode weiter.
Standard-Format-Spezifizierer
Wenn ein Objekt keine eigenen Format-Spezifizierer definiert, wird ein Standardsatz von Format-Spezifizierern verwendet. Diese ähneln konzeptionell den Format-Spezifizierern, die vom bestehenden '%' Operator verwendet werden, aber es gibt auch eine Reihe von Unterschieden.
Die allgemeine Form eines Standard-Format-Spezifizierers ist
[[fill]align][sign][#][0][minimumwidth][.precision][type]
Die Klammern ([]) geben ein optionales Element an.
Dann kann das optionale Ausrichtungsflag eines der folgenden sein:
'<' - Forces the field to be left-aligned within the available
space (This is the default.)
'>' - Forces the field to be right-aligned within the
available space.
'=' - Forces the padding to be placed after the sign (if any)
but before the digits. This is used for printing fields
in the form '+000000120'. This alignment option is only
valid for numeric types.
'^' - Forces the field to be centered within the available
space.
Beachten Sie, dass, sofern keine minimale Feldbreite definiert ist, die Feldbreite immer die gleiche Größe wie die zu füllenden Daten hat, sodass die Ausrichtungsoption in diesem Fall keine Bedeutung hat.
Das optionale 'fill'-Zeichen definiert das Zeichen, das zum Auffüllen des Feldes auf die Mindestbreite verwendet wird. Das Füllzeichen muss, wenn vorhanden, von einem Ausrichtungsflag gefolgt werden.
Die 'sign'-Option ist nur für numerische Typen gültig und kann eine der folgenden sein:
'+' - indicates that a sign should be used for both
positive as well as negative numbers
'-' - indicates that a sign should be used only for negative
numbers (this is the default behavior)
' ' - indicates that a leading space should be used on
positive numbers
Wenn das '#' Zeichen vorhanden ist, verwenden Ganzzahlen die 'alternative Form' für die Formatierung. Das bedeutet, dass die binäre, oktale und hexadezimale Ausgabe mit '0b', '0o' bzw. '0x' präfixiert wird.
'width' ist eine Dezimalzahl, die die minimale Feldbreite definiert. Wenn sie nicht angegeben ist, wird die Feldbreite durch den Inhalt bestimmt.
Wenn das Breitenfeld von einer Null ('0') gefolgt wird, wird die Nullauffüllung aktiviert. Dies entspricht einem Ausrichtungstyp von '=' und einem Füllzeichen von '0'.
Die 'precision' ist eine Dezimalzahl, die angibt, wie viele Ziffern nach dem Dezimalpunkt bei einer Fließkomma-Konvertierung angezeigt werden sollen. Bei nicht-numerischen Typen gibt das Feld die maximale Feldgröße an - mit anderen Worten, wie viele Zeichen aus dem Feldinhalt verwendet werden. Die Präzision wird bei Ganzzahl-Konvertierungen ignoriert.
Schließlich bestimmt der 'type', wie die Daten präsentiert werden sollen.
Die verfügbaren Ganzzahl-Präsentationstypen sind:
'b' - Binary. Outputs the number in base 2.
'c' - Character. Converts the integer to the corresponding
Unicode character before printing.
'd' - Decimal Integer. Outputs the number in base 10.
'o' - Octal format. Outputs the number in base 8.
'x' - Hex format. Outputs the number in base 16, using
lower-case letters for the digits above 9.
'X' - Hex format. Outputs the number in base 16, using
upper-case letters for the digits above 9.
'n' - Number. This is the same as 'd', except that it uses the
current locale setting to insert the appropriate
number separator characters.
'' (None) - the same as 'd'
Die verfügbaren Fließkomma-Präsentationstypen sind:
'e' - Exponent notation. Prints the number in scientific
notation using the letter 'e' to indicate the exponent.
'E' - Exponent notation. Same as 'e' except it converts the
number to uppercase.
'f' - Fixed point. Displays the number as a fixed-point
number.
'F' - Fixed point. Same as 'f' except it converts the number
to uppercase.
'g' - General format. This prints the number as a fixed-point
number, unless the number is too large, in which case
it switches to 'e' exponent notation.
'G' - General format. Same as 'g' except switches to 'E'
if the number gets to large.
'n' - Number. This is the same as 'g', except that it uses the
current locale setting to insert the appropriate
number separator characters.
'%' - Percentage. Multiplies the number by 100 and displays
in fixed ('f') format, followed by a percent sign.
'' (None) - similar to 'g', except that it prints at least one
digit after the decimal point.
Objekte können eigene Format-Spezifizierer definieren, um die Standardwerte zu ersetzen. Ein Beispiel ist die Klasse 'datetime', deren Format-Spezifizierer etwa so aussehen könnten wie die Argumente für die Funktion strftime().
"Today is: {0:%a %b %d %H:%M:%S %Y}".format(datetime.now())
Für alle integrierten Typen erzeugt eine leere Format-Spezifikation das Äquivalent von str(value). Es wird empfohlen, dass Objekte, die eigene Format-Spezifizierer definieren, diese Konvention ebenfalls befolgen.
Explizites Konvertierungsflag
Das explizite Konvertierungsflag wird verwendet, um den Wert des Formatfeldes zu transformieren, bevor er formatiert wird. Dies kann verwendet werden, um das typspezifische Formatierungsverhalten zu überschreiben und den Wert so zu formatieren, als wäre er ein generischerer Typ. Derzeit werden zwei explizite Konvertierungsflags erkannt:
!r - convert the value to a string using repr().
!s - convert the value to a string using str().
Diese Flags werden vor dem Format-Spezifizierer platziert.
"{0!r:20}".format("Hello")
Im vorherigen Beispiel wird die Zeichenkette "Hello" mit Anführungszeichen in einem Feld von mindestens 20 Zeichen Breite ausgegeben.
Eine benutzerdefinierte Formatter-Klasse kann zusätzliche Konvertierungsflags definieren. Der integrierte Formatter löst einen ValueError aus, wenn ein ungültiges Konvertierungsflag angegeben wird.
Formatierung pro Typ steuern
Jeder Python-Typ kann die Formatierung seiner Instanzen steuern, indem er eine Methode __format__ definiert. Die Methode __format__ ist für die Interpretation des Format-Spezifizierers, die Formatierung des Werts und die Rückgabe der resultierenden Zeichenkette verantwortlich.
Die neue, globale integrierte Funktion 'format' ruft einfach diese spezielle Methode auf, ähnlich wie len() und str() einfach ihre jeweiligen speziellen Methoden aufrufen.
def format(value, format_spec):
return value.__format__(format_spec)
Es ist sicher, diese Funktion mit dem Wert "None" aufzurufen (da der Wert "None" in Python ein Objekt ist und Methoden haben kann).
Mehrere integrierte Typen, darunter 'str', 'int', 'float' und 'object', definieren __format__-Methoden. Das bedeutet, dass Ihre Klasse, wenn Sie von einem dieser Typen ableiten, weiß, wie sie sich selbst formatiert.
Die Methode object.__format__ ist die einfachste: Sie konvertiert das Objekt einfach in eine Zeichenkette und ruft dann erneut format auf.
class object:
def __format__(self, format_spec):
return format(str(self), format_spec)
Die __format__-Methoden für 'int' und 'float' führen eine numerische Formatierung basierend auf dem Format-Spezifizierer durch. In einigen Fällen kann diese Formatierung an andere Typen delegiert werden. So kann beispielsweise im Fall, dass der 'int'-Formatter einen Formatt-Typ von 'f' (bedeutet 'float') sieht, der Wert einfach in einen Float umgewandelt und format() erneut aufgerufen werden.
Jede Klasse kann die Methode __format__ überschreiben, um eine benutzerdefinierte Formatierung für diesen Typ bereitzustellen.
class AST:
def __format__(self, format_spec):
...
Hinweis für Python 2.x: Das Argument 'format_spec' ist entweder ein Zeichenkettenobjekt oder ein Unicode-Objekt, abhängig vom Typ der ursprünglichen Formatzeichenkette. Die Methode __format__ sollte den Typ des Spezifiziererparameters testen, um zu bestimmen, ob sie eine Zeichenkette oder ein Unicode-Objekt zurückgeben soll. Es liegt in der Verantwortung der Methode __format__, ein Objekt des richtigen Typs zurückzugeben.
Beachten Sie, dass das oben erwähnte 'explizite Konvertierungs'-Flag nicht an die Methode __format__ übergeben wird. Vielmehr wird erwartet, dass die durch das Flag angegebene Konvertierung vor dem Aufruf von __format__ durchgeführt wird.
Benutzerdefinierte Formatierung
Es wird Zeiten geben, in denen die Anpassung der Formatierung von Feldern pro Typ nicht ausreicht. Ein Beispiel könnte eine Tabellenkalkulationsanwendung sein, die Rauten ('#') anzeigt, wenn ein Wert zu groß ist, um in den verfügbaren Platz zu passen.
Für eine leistungsfähigere und flexiblere Formatierung kann der Zugriff auf die zugrundeliegende Formatierungs-Engine über die Klasse 'Formatter' im Modul 'string' erfolgen. Diese Klasse nimmt zusätzliche Optionen entgegen, die über die normale str.format-Methode nicht zugänglich sind.
Eine Anwendung kann die Klasse Formatter unterklassieren, um ihr eigenes, angepasstes Formatierungsverhalten zu erstellen.
Das PEP versucht nicht, alle Methoden und Eigenschaften der Klasse Formatter exakt zu spezifizieren; stattdessen werden diese in der ersten Implementierung definiert und dokumentiert. Dieses PEP wird jedoch die allgemeinen Anforderungen für die Klasse Formatter festlegen, die unten aufgeführt sind.
Obwohl string.format() die Klasse Formatter nicht direkt zur Formatierung verwendet, verwenden beide dieselbe zugrundeliegende Implementierung. Der Grund, warum string.format() die Klasse Formatter nicht direkt verwendet, ist, dass "string" ein integrierter Typ ist, was bedeutet, dass alle seine Methoden in C implementiert sein müssen, während Formatter eine Python-Klasse ist. Formatter bietet eine erweiterbare Hülle um dieselben C-Funktionen, die von string.format() verwendet werden.
Formatter-Methoden
Die Klasse Formatter nimmt keine Initialisierungsargumente entgegen.
fmt = Formatter()
Die öffentlichen API-Methoden der Klasse Formatter sind wie folgt:
-- format(format_string, *args, **kwargs)
-- vformat(format_string, args, kwargs)
'format' ist die primäre API-Methode. Sie nimmt eine Formatvorlage und eine beliebige Anzahl von positionsbezogenen und Schlüsselwortargumenten entgegen. 'format' ist nur eine Hülle, die 'vformat' aufruft.
'vformat' ist die Funktion, die die eigentliche Formatierungsarbeit leistet. Sie wird als separate Funktion bereitgestellt für Fälle, in denen Sie ein vordefiniertes Wörterbuch von Argumenten übergeben möchten, anstatt das Wörterbuch mithilfe der *args und **kwds Syntax in einzelne Argumente zu zerlegen und neu zu verpacken. 'vformat' zerlegt die Formatvorlagenzeichenkette in Zeichendaten und Ersetzungsfelder. Sie ruft die Methoden 'get_positional' und 'get_index' entsprechend auf (unten beschrieben).
Formatter definiert die folgenden überschreibbaren Methoden:
-- get_value(key, args, kwargs)
-- check_unused_args(used_args, args, kwargs)
-- format_field(value, format_spec)
'get_value' wird verwendet, um einen gegebenen Feldwert abzurufen. Das Argument 'key' ist entweder eine Ganzzahl oder eine Zeichenkette. Wenn es eine Ganzzahl ist, repräsentiert es den Index des positionsbezogenen Arguments in 'args'; Wenn es eine Zeichenkette ist, repräsentiert es ein benanntes Argument in 'kwargs'.
Der Parameter 'args' wird auf die Liste der positionsbezogenen Argumente von 'vformat' gesetzt, und der Parameter 'kwargs' wird auf das Wörterbuch der positionsbezogenen Argumente gesetzt.
Bei zusammengesetzten Feldnamen werden diese Funktionen nur für die erste Komponente des Feldnamens aufgerufen; nachfolgende Komponenten werden durch normale Attribut- und Indexoperationen behandelt.
Das Feldausdruck '0.name' würde zum Beispiel dazu führen, dass 'get_value' mit dem Argument 'key' von 0 aufgerufen wird. Das Attribut 'name' wird nach dem Rückgabewert von 'get_value' durch Aufruf der integrierten Funktion 'getattr' nachgeschlagen.
Wenn der Index oder das Schlüsselwort auf ein Element verweist, das nicht existiert, sollte eine IndexError/KeyError ausgelöst werden.
'check_unused_args' wird verwendet, um die Überprüfung auf ungenutzte Argumente nach Wunsch zu implementieren. Die Argumente dieser Funktion sind die Menge aller Argumentschlüssel, auf die in der Formatzeichenkette tatsächlich verwiesen wurde (Ganzzahlen für positionsbezogene Argumente und Zeichenketten für benannte Argumente), und ein Verweis auf die an vformat übergebenen args und kwargs. Die Menge der ungenutzten Argumente kann aus diesen Parametern berechnet werden. Es wird davon ausgegangen, dass 'check_unused_args' eine Ausnahme auslöst, wenn die Prüfung fehlschlägt.
'format_field' ruft einfach das globale 'format'-Builtin auf. Die Methode wird bereitgestellt, damit Unterklassen sie überschreiben können.
Um ein besseres Verständnis dafür zu bekommen, wie diese Funktionen miteinander in Beziehung stehen, hier ist Pseudocode, der die allgemeine Funktionsweise von vformat erklärt:
def vformat(format_string, args, kwargs):
# Output buffer and set of used args
buffer = StringIO.StringIO()
used_args = set()
# Tokens are either format fields or literal strings
for token in self.parse(format_string):
if is_format_field(token):
# Split the token into field value and format spec
field_spec, _, format_spec = token.partition(":")
# Check for explicit type conversion
explicit, _, field_spec = field_spec.rpartition("!")
# 'first_part' is the part before the first '.' or '['
# Assume that 'get_first_part' returns either an int or
# a string, depending on the syntax.
first_part = get_first_part(field_spec)
value = self.get_value(first_part, args, kwargs)
# Record the fact that we used this arg
used_args.add(first_part)
# Handle [subfield] or .subfield. Assume that 'components'
# returns an iterator of the various subfields, not including
# the first part.
for comp in components(field_spec):
value = resolve_subfield(value, comp)
# Handle explicit type conversion
if explicit == 'r':
value = repr(value)
elif explicit == 's':
value = str(value)
# Call the global 'format' function and write out the converted
# value.
buffer.write(self.format_field(value, format_spec))
else:
buffer.write(token)
self.check_unused_args(used_args, args, kwargs)
return buffer.getvalue()
Beachten Sie, dass der tatsächliche Algorithmus der Formatter-Klasse (der in C implementiert wird) möglicherweise nicht der hier dargestellte ist. (Es ist wahrscheinlich, dass die tatsächliche Implementierung überhaupt keine 'Klasse' sein wird – vielmehr wird vformat möglicherweise nur eine C-Funktion aufrufen, die die anderen überschreibbaren Methoden als Argumente akzeptiert.) Der Hauptzweck dieses Codebeispiels ist die Veranschaulichung der Reihenfolge, in der überschreibbare Methoden aufgerufen werden.
Formatter anpassen
Dieser Abschnitt beschreibt einige typische Möglichkeiten, wie Formatter-Objekte angepasst werden können.
Zur Unterstützung alternativer Formatzeichenketten-Syntaxen kann die Methode 'vformat' überschrieben werden, um die Art und Weise zu ändern, wie Formatzeichenketten geparst werden.
Ein häufiger Wunsch ist die Unterstützung eines 'default'-Namensraums, so dass Sie keine Schlüsselwortargumente an die Methode format() übergeben müssen, sondern stattdessen Werte in einem vorbestehenden Namensraum verwenden können. Dies kann einfach durch Überschreiben von get_value() wie folgt erreicht werden:
class NamespaceFormatter(Formatter):
def __init__(self, namespace={}):
Formatter.__init__(self)
self.namespace = namespace
def get_value(self, key, args, kwds):
if isinstance(key, str):
try:
# Check explicitly passed arguments first
return kwds[key]
except KeyError:
return self.namespace[key]
else:
Formatter.get_value(key, args, kwds)
Dies kann verwendet werden, um einfach eine Formatierungsfunktion zu erstellen, die Zugriff auf globale Variablen erlaubt, zum Beispiel:
fmt = NamespaceFormatter(globals())
greeting = "hello"
print(fmt.format("{greeting}, world!"))
Eine ähnliche Technik kann mit dem locals()-Wörterbuch durchgeführt werden, um auf das locals-Wörterbuch zuzugreifen.
Es wäre auch möglich, einen 'smarten' Namensraum-Formatter zu erstellen, der sowohl auf lokale als auch auf globale Variablen durch Schnüffeln des aufrufenden Stacks zugreifen kann. Aufgrund der Notwendigkeit der Kompatibilität mit den verschiedenen Python-Versionen wird eine solche Fähigkeit nicht in die Standardbibliothek aufgenommen, es wird jedoch erwartet, dass jemand ein Rezept dafür erstellt und veröffentlicht.
Ein weiterer Typ der Anpassung besteht darin, die Art und Weise zu ändern, wie integrierte Typen formatiert werden, indem die Methode 'format_field' überschrieben wird. (Für nicht-integrierte Typen können Sie einfach eine __format__-Sonder-Methode auf diesem Typ definieren.) Sie könnten zum Beispiel die Formatierung von Zahlen überschreiben, um bei Bedarf wissenschaftliche Notation auszugeben.
Fehlerbehandlung
Es gibt zwei Klassen von Ausnahmen, die während der Formatierung auftreten können: Ausnahmen, die vom Formatter-Code selbst generiert werden, und Ausnahmen, die vom Benutzer-Code generiert werden (wie die 'getattr'-Funktion eines Feldobjekts).
Im Allgemeinen sind vom Formatter-Code selbst generierte Ausnahmen vom Typ "ValueError" – es gibt einen Fehler im tatsächlichen "Wert" der Formatzeichenkette. (Dies ist nicht immer wahr; zum Beispiel könnte der Funktion string.format() ein Nicht-String als ersten Parameter übergeben werden, was zu einem TypeError führen würde.)
Der Text, der mit diesen intern generierten ValueError-Ausnahmen verbunden ist, wird den Speicherort der Ausnahme innerhalb der Formatzeichenkette sowie die Art der Ausnahme angeben.
Für vom Benutzer-Code generierte Ausnahmen wird ein Trace-Datensatz und ein Dummy-Frame zum Traceback-Stack hinzugefügt, um bei der Bestimmung des Speicherorts in der Zeichenkette, an dem die Ausnahme aufgetreten ist, zu helfen. Der eingefügte Traceback wird anzeigen, dass der Fehler aufgetreten ist bei:
File "<format_string>;", line XX, in column_YY
wobei XX und YY die Zeilen- bzw. Zeichenpositionsinformationen in der Zeichenkette darstellen.
Alternative Syntax
Natürlich ist eines der am heftigsten umstrittenen Themen die Syntax der Formatzeichenketten, und insbesondere die Markup-Konventionen, die Felder kennzeichnen.
Anstatt zu versuchen, alle verschiedenen Vorschläge erschöpfend aufzulisten, werde ich die am weitesten verbreiteten behandeln.
- Shell-Variablensyntax:
$nameund$(name)(oder in einigen Varianten${name}). Dies ist wahrscheinlich die älteste Konvention und wird von Perl und vielen anderen verwendet. Wenn sie ohne Klammern verwendet wird, wird die Länge der Variablen bestimmt, indem lexikalisch gescannt wird, bis ein ungültiges Zeichen gefunden wird.Dieses Schema wird im Allgemeinen in Fällen verwendet, in denen die Interpolation implizit ist - das heißt, in Umgebungen, in denen jede Zeichenkette Interpolationsvariablen enthalten kann und keine spezielle Ersetzungsfunktion aufgerufen werden muss. In solchen Fällen ist es wichtig, zu verhindern, dass das Interpolationsverhalten versehentlich auftritt. Daher wird das '$' (das ansonsten ein relativ selten verwendetes Zeichen ist) verwendet, um anzuzeigen, wann das Verhalten auftreten soll.
Es ist jedoch die Meinung des Autors, dass in Fällen, in denen die Formatierung explizit aufgerufen wird, weniger Sorgfalt erforderlich ist, um eine versehentliche Interpolation zu verhindern, in denen eine leichtere und weniger umständliche Syntax verwendet werden kann.
- printf und seine Cousins ('%'), einschließlich Variationen, die einen Feldindex hinzufügen, sodass Felder außer der Reihe interpoliert werden können.
- Andere reine Klammer-Varianten. Verschiedene MUDs (Multi-User Dungeons) wie MUSH haben Klammern (z. B.
[name]) zur Zeichenketteninterpolation verwendet. Die Microsoft .Net-Bibliotheken verwenden geschweifte Klammern ({}) und eine Syntax, die der in diesem Vorschlag sehr ähnlich ist, obwohl die Syntax für Format-Spezifizierer ganz anders ist. [4] - Backquoting. Diese Methode hat den Vorteil minimaler syntaktischer Unordnung, aber ihr fehlen viele der Vorteile einer Funktionsaufrufsyntax (wie komplexe Ausdrucksargumente, benutzerdefinierte Formatierer usw.).
- Andere Variationen umfassen Rubys
#{}, PHP's{$name}und so weiter.
Einige spezifische Aspekte der Syntax verdienen zusätzliche Kommentare:
1) Backslash-Zeichen für Maskierungen. Die ursprüngliche Version dieses PEP verwendete Backslash anstelle von Verdopplung, um eine Klammer zu maskieren. Dies funktionierte, weil Backslashes in Python-Zeichenkettenliteralen, die keiner Standard-Backslash-Sequenz wie \n entsprechen, unverändert bleiben. Dies führte jedoch zu einer gewissen Verwirrung und potenziellen Situationen mit mehreren rekursiven Maskierungen, z. B. \\\\{, um einen literalen Backslash vor eine Klammer zu setzen.
2) Die Verwendung des Doppelpunkt-Zeichens (':') als Trennzeichen für Format-Spezifizierer. Dies wurde einfach gewählt, weil es das ist, was .Net verwendet.
Vorschläge für alternative Funktionen
Beschränkung des Attributzugriffs: Eine frühere Version des PEP beschränkte die Möglichkeit, Attribute, die mit einem führenden Unterstrich beginnen, zugegriffen, z. B. "{0}._private". Dies ist jedoch eine nützliche Fähigkeit beim Debugging, daher wurde die Funktion fallen gelassen.
Einige Entwickler schlugen vor, die Möglichkeit des 'getattr'- und 'getitem'-Zugriffs vollständig zu streichen. Dies steht jedoch im Widerspruch zu den Bedürfnissen einer anderen Gruppe von Entwicklern, die sich stark für die Möglichkeit aussprachen, ein großes dict als einzelnes Argument zu übergeben (ohne es in einzelne Schlüsselwortargumente mit der **kwargs-Syntax zu zerlegen) und dann die Formatzeichenkette auf dict-Einträge einzeln verweisen zu lassen.
Es gab auch Vorschläge, den Satz von Ausdrücken zu erweitern, die in einer Formatzeichenkette erlaubt sind. Dies wurde jedoch als Verstoß gegen den Geist von TOOWTDI angesehen, da der gleiche Effekt in den meisten Fällen durch Ausführung des gleichen Ausdrucks auf dem Parameter erreicht werden kann, bevor er an die Formatierungsfunktion übergeben wird. Für Fälle, in denen die Formatzeichenkette zur willkürlichen Formatierung in einer datenreichen Umgebung verwendet wird, wird die Verwendung einer spezialisierten Vorlagen-Engine wie Genshi [5] oder Cheetah [6] empfohlen.
Viele andere Funktionen wurden in Betracht gezogen und abgelehnt, da sie leicht durch Unterklassifizierung von Formatter erreicht werden konnten, anstatt die Funktion in die Basisimplementierung einzubauen. Dazu gehören alternative Syntaxen, Kommentare in Formatzeichenketten und viele andere.
Sicherheitsüberlegungen
Historisch gesehen war die Zeichenkettenformatierung eine häufige Quelle für Sicherheitslücken in webbasierten Anwendungen, insbesondere wenn das Zeichenkettenformatierungssystem die Einbettung beliebiger Ausdrücke in Formatzeichenketten zulässt.
Der beste Weg, Zeichenkettenformatierung so zu verwenden, dass keine potenziellen Sicherheitslücken entstehen, ist, niemals Formatzeichenketten zu verwenden, die aus einer nicht vertrauenswürdigen Quelle stammen.
Alternativ dazu ist der nächste beste Ansatz sicherzustellen, dass die Zeichenkettenformatierung keine Nebeneffekte hat. Aufgrund der offenen Natur von Python ist es unmöglich zu garantieren, dass irgendeine nicht-triviale Operation diese Eigenschaft hat. Dieses PEP beschränkt die Arten von Ausdrücken in Formatzeichenketten auf diejenigen, bei denen sichtbare Nebeneffekte sowohl selten als auch von der Kultur der Python-Entwickler stark entmutigt sind. So wird beispielsweise der Attributzugriff erlaubt, da es pathologisch wäre, Code zu schreiben, bei dem allein der Zugriff auf ein Attribut sichtbare Nebeneffekte hat (ob der Code **unsichtbare** Nebeneffekte hat - wie das Erstellen eines Caches für schnellere Abfragen - ist irrelevant).
Beispielimplementierung
Eine Implementierung einer früheren Version dieses PEP wurde von Patrick Maupin und Eric V. Smith erstellt und ist im pep3101-Sandbox unter folgendem Link zu finden:
Abwärtskompatibilität
Die Abwärtskompatibilität kann durch Beibehaltung der bestehenden Mechanismen aufrechterhalten werden. Das neue System kollidiert nicht mit den Methodennamen der bestehenden Zeichenkettenformatierungstechniken, sodass beide Systeme koexistieren können, bis es an der Zeit ist, das ältere System zu verwerfen.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Source: https://github.com/python/peps/blob/main/peps/pep-3101.rst
Last modified: 2025-02-01 08:59:27 GMT