web-dev-qa-db-fra.com

Comment obtenir Python pour formater gracieusement les champs None et non existants

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.

31
Juan A. Navarro

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.

35
dawg

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, ~, ~, ~
8
Martijn Pieters

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'
5
Simeon Visser