Comment formater une chaîne à l'aide d'un dictionnaire en python-3.x?

215

Je suis un grand fan de l'utilisation de dictionnaires pour formater les chaînes. Cela m'aide à lire le format de chaîne que j'utilise et me permet de profiter des dictionnaires existants. Par exemple:

class MyClass:
    def __init__(self):
        self.title = 'Title'

a = MyClass()
print 'The title is %(title)s' % a.__dict__

path = '/path/to/a/file'
print 'You put your file here: %(path)s' % locals()

Cependant, je ne peux pas comprendre la syntaxe python 3.x pour faire la même chose (ou si c'est même possible). Je voudrais faire ce qui suit

# Fails, KeyError 'latitude'
geopoint = {'latitude':41.123,'longitude':71.091}
print '{latitude} {longitude}'.format(geopoint)

# Succeeds
print '{latitude} {longitude}'.format(latitude=41.123,longitude=71.091)
Doran
la source

Réponses:

15

Étant donné que la question est spécifique à Python 3, voici la nouvelle syntaxe de chaîne f , disponible depuis Python 3.6:

>>> geopoint = {'latitude':41.123,'longitude':71.091}
>>> print(f'{geopoint["latitude"]} {geopoint["longitude"]}')
41.123 71.091

Notez les guillemets simples extérieurs et les guillemets doubles intérieurs (vous pouvez également le faire dans l'autre sens).

Bois de wyrm
la source
Je dirais que l'utilisation de la f-string est plus alignée sur l'approche python3.
CD Jonatas
2
Gardez à l'esprit que les chaînes f sont nouvelles dans Python 3.6, et non dans 3.5.
Hugo
410

Est-ce que c'est bon pour toi?

geopoint = {'latitude':41.123,'longitude':71.091}
print('{latitude} {longitude}'.format(**geopoint))
cocoatomo
la source
2
J'ai essayé ça et ça a marché. Mais je ne comprends pas l'utilisation de la «notation de pointeur». Je sais que Python n'utilise pas de pointeurs, est-ce un exemple de kwargs?
Homunculus Reticulli
2
@HomunculusReticulli Il s'agit d'un paramètre de format (largeur de champ minimale), pas d'un pointeur vers un style C ++ de pointeur. docs.python.org/release/2.4.4/lib/typesseq-strings.html
D.Rosado
29
Introduction de Python 3.2 format_map. Similaire à str.format(**mapping), sauf qu'il mappingest utilisé directement et non copié dans a dict. Ceci est utile si, par exemple, il mappings'agit d'une sous
diapir
1
@eugene Que fait ** à un dictionnaire python? Je ne pense pas que cela crée un objet car l'impression (** géopoint) échoue en donnant une erreur de syntaxe
Nityesh Agarwal
4
@NityeshAgarwal, il diffuse le dictionnaire avec les paires nom = valeur comme arguments individuels, c'est print(**geopoint)-à- dire comme print(longitude=71.091, latitude=41.123). Dans de nombreuses langues, il est connu comme opérateur splat . En JavaScript, cela s'appelle l' opérateur de propagation . En python, aucun nom particulier n'est donné à cet opérateur.
abhisekp
79

Pour décompresser un dictionnaire en arguments de mots clés, utilisez **. De plus, la mise en forme de nouveau style prend en charge la référence aux attributs des objets et des éléments des mappages:

'{0[latitude]} {0[longitude]}'.format(geopoint)
'The title is {0.title}s'.format(a) # the a from your first example

la source
2
Je trouve cette réponse meilleure, car l'ajout de l'index positionnel pour l'espace réservé rend le code plus explicite et plus facile à utiliser. Surtout si on a quelque chose comme ça:'{0[latitude]} {1[latitude]} {0[longitude]} {1[longitude]}'.format(geopoint0, geopoint1)
Løiten
1
C'est utile si vous utilisez un defaultdictet que vous n'avez pas toutes les clés
Whymarrh
65

Comme Python 3.0 et 3.1 sont en fin de vie et que personne ne les utilise, vous pouvez et devez utiliser str.format_map(mapping)(Python 3.2+):

Similaire à str.format(**mapping), sauf que le mappage est utilisé directement et non copié dans adict . Ceci est utile si, par exemple, le mappage est une dictsous - classe.

Cela signifie que vous pouvez utiliser par exemple un defaultdictqui définirait (et renverrait) une valeur par défaut pour les clés manquantes:

>>> from collections import defaultdict
>>> vals = defaultdict(lambda: '<unset>', {'bar': 'baz'})
>>> 'foo is {foo} and bar is {bar}'.format_map(vals)
'foo is <unset> and bar is baz'

Même si le mappage fourni est une dict, et non une sous-classe, ce serait probablement encore un peu plus rapide.

La différence n'est pas grande cependant, étant donné

>>> d = dict(foo='x', bar='y', baz='z')

puis

>>> 'foo is {foo}, bar is {bar} and baz is {baz}'.format_map(d)

est environ 10 ns (2%) plus rapide que

>>> 'foo is {foo}, bar is {bar} and baz is {baz}'.format(**d)

sur mon Python 3.4.3. La différence serait probablement plus grande car il y a plus de clés dans le dictionnaire, et


Notez que le langage de format est bien plus flexible que cela cependant; ils peuvent contenir des expressions indexées, des accès aux attributs, etc., vous pouvez donc formater un objet entier ou 2 d'entre eux:

>>> p1 = {'latitude':41.123,'longitude':71.091}
>>> p2 = {'latitude':56.456,'longitude':23.456}
>>> '{0[latitude]} {0[longitude]} - {1[latitude]} {1[longitude]}'.format(p1, p2)
'41.123 71.091 - 56.456 23.456'

À partir de 3.6, vous pouvez également utiliser les chaînes interpolées:

>>> f'lat:{p1["latitude"]} lng:{p1["longitude"]}'
'lat:41.123 lng:71.091'

Vous devez juste vous rappeler d'utiliser les autres caractères de guillemet dans les guillemets imbriqués. Un autre avantage de cette approche est qu'elle est beaucoup plus rapide que d' appeler une méthode de formatage.

Antti Haapala
la source
Bien, y a-t-il des améliorations de performances par rapport à format? (Étant donné qu'il n'est pas copié dans un dict)
Bhargav Rao
2
@BhargavRao pas beaucoup, 2%: D
Antti Haapala
@BhargavRao si vous recherchez des performances, utilisez ceci '%(latitude)s %(longitude)s'%geopoint;)
Tcll
20
print("{latitude} {longitude}".format(**geopoint))
S.Lott
la source
6

La syntaxe Python 2 fonctionne également dans Python 3:

>>> class MyClass:
...     def __init__(self):
...         self.title = 'Title'
... 
>>> a = MyClass()
>>> print('The title is %(title)s' % a.__dict__)
The title is Title
>>> 
>>> path = '/path/to/a/file'
>>> print('You put your file here: %(path)s' % locals())
You put your file here: /path/to/a/file
Lennart Regebro
la source
De plus, il est également nettement plus performant que f""ou "".format();)
Tcll
2
geopoint = {'latitude':41.123,'longitude':71.091}

# working examples.
print(f'{geopoint["latitude"]} {geopoint["longitude"]}') # from above answer
print('{geopoint[latitude]} {geopoint[longitude]}'.format(geopoint=geopoint)) # alternate for format method  (including dict name in string).
print('%(latitude)s %(longitude)s'%geopoint) # thanks @tcll
Cheikh Abdul Wahid
la source
1
vous en avez manqué un;) print('%(latitude)s %(longitude)s'%geopoint)c'est aussi beaucoup plus rapide que les 2 autres
Tcll
@tcll En fait, je voulais les exemples, où je peux utiliser le nom du dictionnaire à l'intérieur de la chaîne. Quelque chose comme ça'%(geopoint["latitude"])s %(geopoint["longitude"])s'%{"geopoint":geopoint}
Sheikh Abdul Wahid
1

La plupart des réponses ont formaté uniquement les valeurs du dict.

Si vous souhaitez également formater la clé dans la chaîne, vous pouvez utiliser dict.items () :

geopoint = {'latitude':41.123,'longitude':71.091}
print("{} {}".format(*geopoint.items()))

Production:

('latitude', 41.123) ('longitude', 71.091)

Si vous souhaitez formater de manière arbitraire, c'est-à-dire sans afficher les valeurs-clés comme les tuples:

from functools import reduce
print("{} is {} and {} is {}".format(*reduce((lambda x, y: x + y), [list(item) for item in geopoint.items()])))

Production:

la latitude est 41.123 et la longitude est 71.091

victortv
la source
notez qu'il y a une chance que 'longitude' puisse venir avant 'latitude' de geopoint.items();)
Tcll