Einführung
Das Zeichenfolgenmodul verfügt über eine Vorlagenklasse, mit der Sie mithilfe eines Zuordnungsobjekts Ersetzungen in einer Zeichenfolge vornehmen können, z.
>>> string.Template('var is $var').substitute({'var': 1})
'var is 1'
Die Ersatzmethode kann eine KeyError-Ausnahme auslösen, wenn beispielsweise versucht wird, ein Element zu ersetzen, das in der Zuordnung fehlt
>>> string.Template('var is $var and foo is $foo').substitute({'var': 1})
KeyError: 'foo'
Oder kann einen ValueError auslösen, wenn die Vorlagenzeichenfolge ungültig ist, z. Es enthält ein $
Zeichen gefolgt von einem Leerzeichen:
>>> string.Template('$ var is $var').substitute({'var': 1})
ValueError: Invalid placeholder in string: line 1, col 1
Das Problem
Mit einer Vorlagenzeichenfolge und einer Zuordnung möchte ich bestimmen, ob alle Platzhalter in der Vorlage ersetzt werden. Zu diesem Zweck würde ich versuchen, die Ersetzung vorzunehmen und eine KeyError-Ausnahme abzufangen:
def check_substitution(template, mapping):
try:
string.Template(template).substitute(mapping)
except KeyError:
return False
except ValueError:
pass
return True
Dies funktioniert jedoch nicht, da nachfolgende KeyErrors nicht abgefangen werden, wenn die Vorlage ungültig ist und ein ValueError ausgelöst wird:
>>> check_substitution('var is $var and foo is $foo', {'var': 1})
False
>>> check_substitution('$ var is $var and foo is $foo', {'var': 1})
True
Aber ValueErrors sind mir egal . Was wäre der richtige Ansatz für dieses Problem?
3 Antworten
In den Dokumenten heißt es, dass Sie das Muster ersetzen können, solange es alle enthält notwendige benannte Gruppen:
import re
from string import Template
class TemplateIgnoreInvalid(Template):
# override pattern to make sure `invalid` never matches
pattern = r"""
%(delim)s(?:
(?P<escaped>%(delim)s) | # Escape sequence of two delimiters
(?P<named>%(id)s) | # delimiter and a Python identifier
{(?P<braced>%(id)s)} | # delimiter and a braced identifier
(?P<invalid>^$) # never matches (the regex is not multilined)
)
""" % dict(delim=re.escape(Template.delimiter), id=Template.idpattern)
def check_substitution(template, **mapping):
try:
TemplateIgnoreInvalid(template).substitute(mapping)
except KeyError:
return False
else:
return True
Tests
f = check_substitution
assert f('var is $var', var=1)
assert f('$ var is $var', var=1)
assert f('var is $var and foo is $foo', var=1, foo=2)
assert not f('var is $var and foo is $foo', var=1)
assert f('$ var is $var and foo is $foo', var=1, foo=2)
assert not f('$ var is $var and foo is $foo', var=1)
# support all invalid patterns
assert f('var is $var and foo is ${foo', var=1)
assert f('var is $var and foo is ${foo', var=1, foo=2) #NOTE: problematic API
assert f('var is $var and foo is ${foo and ${baz}', var=1, baz=3)
assert not f('var is $var and foo is ${foo and ${baz}', var=1)
Es funktioniert für alle ungültigen Vorkommen des Trennzeichens ($
).
Die Beispiele zeigen, dass das Ignorieren ungültiger Muster einfache Tippfehler in der Vorlage verbirgt, sodass dies keine gute API ist.
Dies ist eine schnelle Lösung (mit Rekursion):
def check_substitution(tem, m):
try:
string.Template(tem).substitute(m)
except KeyError:
return False
except ValueError:
return check_substitution(tem.replace('$ ', '$'), m) #strip spaces after $
return True
Ich weiß, dass es länger dauert, wenn zwischen $
und var
mehr als ein Leerzeichen steht. Sie können es also verbessern, indem Sie den regulären Ausdruck verwenden.
BEARBEITEN
Es ist sinnvoller, $
in $$
zu maskieren [Danke @Pedro], damit Sie ValueError
anhand dieser Anweisung abfangen können:
return check_substitution(tem.replace('$ ', '$$ '), m) #escaping $ by $$
Python führt keine Zeichenfolgenersetzung über mehrere Zeilen durch
Wenn Sie diese Zeichenfolge haben
criterion = """
<criteria>
<order>{order}</order>
<body><![CDATA[{code}]]></body>
</criteria>
"""
criterion.format(dict(order="1",code="Hello")
Ergebnisse in:
KeyError: 'order'
Eine Lösung besteht darin, das Modul string.Template zu verwenden
from string import Template
criterion = """
<criteria>
<order>$order</order>
<body><![CDATA[$code]]></body>
</criteria>
"""
Template(criterion).substitute(dict(order="1",code="hello")
HINWEIS: Sie müssen den Schlüsselwörtern ein $ voranstellen, ohne sie in {} einzuschließen
Ausgabe ist:
<criteria>
<order>1</order>
<body><![CDATA[hello]]></body>
</criteria>
Vollständige Dokumente sind: https://docs.python.org/2/ library / string.html # template-strings
Verwandte Fragen
Verknüpfte Fragen
Neue Fragen
python
Python ist eine dynamisch typisierte Mehrzweck-Programmiersprache mit mehreren Paradigmen. Es wurde entwickelt, um schnell zu lernen, zu verstehen, zu verwenden und eine saubere und einheitliche Syntax durchzusetzen. Bitte beachten Sie, dass Python 2 ab dem 01.01.2020 offiziell nicht mehr unterstützt wird. Fügen Sie für versionenspezifische Python-Fragen das Tag [python-2.7] oder [python-3.x] hinzu. Wenn Sie eine Python-Variante (z. B. Jython, PyPy) oder eine Bibliothek (z. B. Pandas und NumPy) verwenden, fügen Sie diese bitte in die Tags ein.