Si j'écris en Python:
data = {'n': 3, 'k': 3.141594, 'p': {'a': 7, 'b': 8}}
print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data))
del data['k']
data['p']['b'] = None
print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data))
Je reçois:
3, 3.14, 7, 8
Traceback (most recent call last):
File "./funky.py", line 186, in <module>
print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data))
KeyError: 'k'
Au lieu d'un message d'erreur, comment puis-je obtenir Python pour formater plus gracieusement les champs Aucun et inexistant?
Pour donner un exemple, je voudrais voir dans la sortie quelque chose de plus comme:
3, 3.14, 7, 8
3, ~, 7, ~
Idéalement, bien sûr, je voudrais pouvoir spécifier la chaîne utilisée au lieu de ces valeurs manquantes.
La recommandation dans PEP 3101 est de sous-classer Formateur :
import string
class PartialFormatter(string.Formatter):
def __init__(self, missing='~~', bad_fmt='!!'):
self.missing, self.bad_fmt=missing, bad_fmt
def get_field(self, field_name, args, kwargs):
# Handle a key not found
try:
val=super(PartialFormatter, self).get_field(field_name, args, kwargs)
# Python 3, 'super().get_field(field_name, args, kwargs)' works
except (KeyError, AttributeError):
val=None,field_name
return val
def format_field(self, value, spec):
# handle an invalid format
if value==None: return self.missing
try:
return super(PartialFormatter, self).format_field(value, spec)
except ValueError:
if self.bad_fmt is not None: return self.bad_fmt
else: raise
fmt=PartialFormatter()
data = {'n': 3, 'k': 3.141594, 'p': {'a': '7', 'b': 8}}
print(fmt.format('{n}, {k:.2f}, {p[a]}, {p[b]}', **data))
# 3, 3.14, 7, 8
del data['k']
data['p']['b'] = None
print(fmt.format('{n}, {k:.2f}, {p[a]:.2f}, {p[b]}', **data))
# 3, ~~, !!, ~~
Une fois configuré, il imprimera ~~
si aucun champ ou attribut n'est trouvé et !!
si un format non valide est utilisé étant donné la valeur du champ. (Utilisez simplement None
pour l'argument de mot clé bad_fmt
si vous voulez que la valeur par défaut d'une erreur de valeur soit levée.)
Pour gérer les clés manquantes, vous devez sous-classer les deux get_field
pour attraper les KeyError
ou AttributeError
et format_field
pour renvoyer une valeur par défaut pour la clé manquante.
Puisque vous attrapez format_field
erreurs, vous pouvez également intercepter un champ de format incorrect en interceptant le ValueError
de la superclasse.
La méthode str.format()
ne vous donne pas de méthode directe pour gérer les clés manquantes ou remplacer les valeurs.
Vous pouvez ajouter une couche d'indirection; passez un mappage qui gère les valeurs manquantes et None
et modifiez le format pour utiliser uniquement cet argument:
class PlaceholderFormatValue():
def __format__(self, spec):
return '~'
def __getitem__(self, name):
# handle further nested item access
return self
class formatting_dict(dict):
def __getitem__(self, name):
value = self.get(name)
if isinstance(value, dict):
# rewrap nested dictionaries to handle missing nested keys
value = type(self)(value)
return value if value is not None else PlaceholderFormatValue()
print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))
Tous les emplacements font maintenant référence à l'argument positionnel 0
, Qui est traité comme un dictionnaire, mais les recherches de clés réussissent toujours et les valeurs manquantes et None
sont remplacées par une valeur d'espace réservé.
Ici, la PlaceholderFormatValue()
garantit que, quelle que soit la spécification de format, la valeur peut être interpolée dans le format. Cela fait fonctionner {0[k]:.2f}
, Par exemple.
En encapsulant toutes les valeurs dict
et en ayant PlaceholderFormatValue
gérer l'accès aux éléments, ce qui précède peut également gérer l'échec de la fourniture de clés imbriquées ou de dictionnaires entiers:
>>> data = {'n': 3, 'k': 3.141594, 'p': {'a': 7, 'b': 8}}
>>> del data['k']
>>> data['p']['b'] = None
>>> print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))
3, ~, 7, ~
>>> del data['p']['a']
>>> print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))
3, ~, ~, ~
>>> del data['p']
>>> print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))
3, ~, ~, ~
Si vous pouvez effectuer le formatage séparément, vous pouvez utiliser Template.safe_substitute
qui gère gracieusement les valeurs manquantes:
>>> from string import Template
>>> t = Template("$a $b $c")
>>> t.safe_substitute(a=3)
'3 $b $c'