¿Cómo formateo una cadena usando un diccionario en python-3.x?

215

Soy un gran fanático del uso de diccionarios para formatear cadenas. Me ayuda a leer el formato de cadena que estoy usando, así como a aprovechar los diccionarios existentes. Por ejemplo:

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()

Sin embargo, no puedo entender la sintaxis de python 3.x para hacer lo mismo (o si eso es posible). Me gustaria hacer lo siguiente

# 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
fuente

Respuestas:

15

Dado que la pregunta es específica de Python 3, aquí está usando la nueva sintaxis de f-string , disponible desde Python 3.6:

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

Tenga en cuenta las comillas simples externas y las comillas dobles internas (también puede hacerlo al revés).

Wyrmwood
fuente
Yo diría que el uso de la cadena f está más alineado con el enfoque python3.
CD de Jonatas
2
Tenga en cuenta que las cadenas f son nuevas en Python 3.6 y no en 3.5.
Hugo
409

¿Esto es bueno para ti?

geopoint = {'latitude':41.123,'longitude':71.091}
print('{latitude} {longitude}'.format(**geopoint))
cocoatomo
fuente
2
Probé esto y funcionó. Pero no entiendo el uso de la 'notación de puntero'. Sé que Python no usa punteros, ¿es este un ejemplo de kwargs?
Homunculus Reticulli
2
@HomunculusReticulli Ese es un parámetro de formato (Ancho de campo mínimo), no un puntero a un puntero estilo C ++. docs.python.org/release/2.4.4/lib/typesseq-strings.html
D.Rosado
29
Python 3.2 introducido format_map. Similar a str.format(**mapping), excepto que mappingse usa directamente y no se copia a dict. Esto es útil si, por ejemplo, mappinges una subclase dict
diapir
1
@eugene ¿Qué le hace ** a un diccionario de Python? No creo que cree un objeto porque la impresión (** geopunto) falla al dar un error de sintaxis
Nityesh Agarwal
44
@NityeshAgarwal difunde el diccionario con los pares nombre = valor como argumentos individuales, print(**geopoint)es decir, es igual que print(longitude=71.091, latitude=41.123). En muchos idiomas, se conoce como operador splat . En JavaScript, se llama operador de propagación . En python, no hay un nombre particular dado a este operador.
abhisekp
79

Para descomprimir un diccionario en argumentos de palabras clave, use **. Además, el formato de nuevo estilo admite referencias a atributos de objetos y elementos de asignaciones:

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

fuente
2
Encuentro esta respuesta mejor, ya que agregar el índice posicional para el marcador de posición hace que el código sea más explícito y más fácil de usar. Especialmente si uno tiene algo como esto:'{0[latitude]} {1[latitude]} {0[longitude]} {1[longitude]}'.format(geopoint0, geopoint1)
Løiten
1
Esto es útil si está utilizando un defaultdicty no tiene todas las claves
Whymarrh
65

Como Python 3.0 y 3.1 tienen EOL y nadie los usa, usted puede y debe usar str.format_map(mapping)(Python 3.2+):

Similar a str.format(**mapping), excepto que la asignación se usa directamente y no se copia adict . Esto es útil si, por ejemplo, el mapeo es una dictsubclase.

Lo que esto significa es que puede usar, por ejemplo, un valor defaultdictque establecería (y devolvería) un valor predeterminado para las claves que faltan:

>>> 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'

Incluso si el mapeo proporcionado es una dictsubclase, no una subclase, probablemente aún sería un poco más rápido.

Sin embargo, la diferencia no es grande, dado

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

luego

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

es aproximadamente 10 ns (2%) más rápido que

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

en mi Python 3.4.3. La diferencia probablemente sería mayor a medida que haya más claves en el diccionario, y


Tenga en cuenta que el lenguaje de formato es mucho más flexible que eso; pueden contener expresiones indexadas, accesos de atributos, etc., para que pueda formatear un objeto completo o 2 de ellos:

>>> 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'

A partir de 3.6 también puede usar las cadenas interpoladas:

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

Solo necesita recordar usar los otros caracteres de comillas dentro de las comillas anidadas. Otra ventaja de este enfoque es que es mucho más rápido que llamar a un método de formateo.

Antti Haapala
fuente
Agradable, ¿hay alguna mejora en el rendimiento format? (Dado que no se copia a un dict)
Bhargav Rao
2
@BhargavRao no mucho, 2%: D
Antti Haapala
@BhargavRao si está buscando rendimiento, use esto '%(latitude)s %(longitude)s'%geopoint;)
Tcll
20
print("{latitude} {longitude}".format(**geopoint))
S.Lott
fuente
6

La sintaxis de Python 2 también funciona en 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
fuente
Además, es notablemente más f"""".format()
eficiente
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
Sheikh Abdul Wahid
fuente
1
te perdiste uno;) print('%(latitude)s %(longitude)s'%geopoint)esto también es significativamente más rápido que los otros 2
Tcll
@tcll En realidad, quería los ejemplos, donde puedo usar el nombre del diccionario dentro de la cadena. Algo como esto'%(geopoint["latitude"])s %(geopoint["longitude"])s'%{"geopoint":geopoint}
Sheikh Abdul Wahid
1

La mayoría de las respuestas formatearon solo los valores del dict.

Si también desea formatear la clave en la cadena, puede usar dict.items () :

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

Salida:

('latitud', 41.123) ('longitud', 71.091)

Si desea formatear de forma arbitraria, es decir, no mostrar los valores-clave como las tuplas:

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

Salida:

la latitud es 41.123 y la longitud es 71.091

victortv
fuente
tenga en cuenta que existe la posibilidad de que 'longitud' pueda venir antes de 'latitud' desde geopoint.items();)
Tcll