Como formatar uma string usando um dicionário em python-3.x?

215

Eu sou um grande fã de usar dicionários para formatar strings. Isso me ajuda a ler o formato da string que estou usando, além de me tirar proveito dos dicionários existentes. Por exemplo:

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

No entanto, não consigo descobrir a sintaxe python 3.x para fazer o mesmo (ou se isso for possível). Eu gostaria de fazer o seguinte

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

Respostas:

15

Como a pergunta é específica para o Python 3, aqui está a nova sintaxe da string f , disponível desde o Python 3.6:

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

Observe as aspas simples externas e as aspas duplas internas (você também pode fazer o contrário).

Wyrmwood
fonte
Eu diria que o uso da string f está mais alinhado à abordagem python3.
Jonatas CD
2
Lembre-se de que as f-strings são novas no Python 3.6 e não no 3.5.
Hugo
409

Isso é bom para você?

geopoint = {'latitude':41.123,'longitude':71.091}
print('{latitude} {longitude}'.format(**geopoint))
cocoatomo
fonte
2
Tentei isso e funcionou. Mas não entendo o uso da 'notação do ponteiro'. Eu sei que o Python não usa ponteiros, este é um exemplo de kwargs?
Homunculus Reticulli
2
@HomunculusReticulli Esse é um parâmetro de formato (largura mínima do campo), não um ponteiro para um estilo C ++ de ponteiro. docs.python.org/release/2.4.4/lib/typesseq-strings.html
D.Rosado
29
Introduzido o Python 3.2 format_map. Semelhante a str.format(**mapping), exceto que mappingé usado diretamente e não copiado para a dict. Isto é útil se, por exemplo, mappingé uma subclasse dict
diapir
1
@eugene O que ** faz em um dicionário python? Eu não acho que ele cria um objeto porque print (GeoPoint **) não dando erro de sintaxe
Nityesh Agarwal
4
@NityeshAgarwal ele espalha o dicionário com os pares nome = valor como argumentos individuais, ou seja, print(**geopoint)é o mesmo que print(longitude=71.091, latitude=41.123). Em muitos idiomas, é conhecido como operador splat . Em JavaScript, é chamado de operador de propagação . Em python, não há um nome específico dado a esse operador.
abhisekp
79

Para descompactar um dicionário em argumentos de palavras-chave, use **. Além disso, a formatação de novo estilo suporta a referência a atributos de objetos e itens de mapeamentos:

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

fonte
2
Acho essa resposta melhor, pois adicionar o índice posicional para o espaço reservado torna o código mais explícito e mais fácil de usar. Especialmente se alguém tem algo como isto:'{0[latitude]} {1[latitude]} {0[longitude]} {1[longitude]}'.format(geopoint0, geopoint1)
Løiten
1
Isso é útil se você estiver usando um defaultdicte não tem todas as chaves
Whymarrh
65

Como o Python 3.0 e 3.1 são EOL'ed e ninguém os utiliza, você pode e deve usar str.format_map(mapping)(Python 3.2+):

Semelhante a str.format(**mapping), exceto que o mapeamento é usado diretamente e não copiado para adict . Isso é útil se, por exemplo, o mapeamento for uma dictsubclasse.

O que isso significa é que você pode usar, por exemplo, um defaultdictque definiria (e retornaria) um valor padrão para chaves ausentes:

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

Mesmo que o mapeamento fornecido seja uma dictsubclasse e não uma subclasse, isso provavelmente ainda seria um pouco mais rápido.

A diferença não é grande, dado

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

então

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

é cerca de 10 ns (2%) mais rápido que

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

no meu Python 3.4.3. A diferença provavelmente seria maior, pois há mais chaves no dicionário e


Observe que o idioma do formato é muito mais flexível do que isso; eles podem conter expressões indexadas, acessos a atributos e assim por diante, para que você possa formatar um objeto inteiro ou 2 deles:

>>> 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 da versão 3.6, você também pode usar as seqüências interpoladas:

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

Você só precisa se lembrar de usar os outros caracteres de aspas nas aspas aninhadas. Outra vantagem dessa abordagem é que ela é muito mais rápida do que chamar um método de formatação.

Antti Haapala
fonte
Bom, há alguma melhoria de desempenho terminada format? (Dado que não é copiado para um ditado)
Bhargav Rao
2
@BhargavRao não muito, 2%: D
Antti Haapala
@BhargavRao se você está procurando desempenho, use isso '%(latitude)s %(longitude)s'%geopoint;)
Tcll 17/04
20
print("{latitude} {longitude}".format(**geopoint))
S.Lott
fonte
6

A sintaxe do Python 2 também funciona no 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
fonte
além disso, é também notavelmente mais desempenho do que f""ou "".format();)
Tcll 17/04
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
fonte
1
você perdeu um;) print('%(latitude)s %(longitude)s'%geopoint)isso também é significativamente mais rápido que o outro 2
Tcll 17/04
@tcll Na verdade, eu queria os exemplos, onde posso usar o nome do dicionário dentro da string. Algo assim'%(geopoint["latitude"])s %(geopoint["longitude"])s'%{"geopoint":geopoint}
Sheikh Abdul Wahid
1

A maioria das respostas formatou apenas os valores do ditado.

Se você também deseja formatar a chave na cadeia, use dict.items () :

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

Resultado:

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

Se você deseja formatar de forma arbitral, ou seja, não mostra os valores-chave como tuplas:

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

Resultado:

latitude é 41.123 e longitude é 71.091

victortv
fonte
note que há uma chance de que 'longitude' possa vir antes de 'latitude' geopoint.items();)
Tcll 17/04