Formatage des chaînes de Python: % vs..format

Python 2.6 introduit la méthode str.format() avec une syntaxe légèrement différente de l'opérateur % existant. Qui est le meilleur et pour quelles situations?

  1. les méthodes suivantes sont utilisées et ont le même résultat, alors quelle est la différence?

    #!/usr/bin/python
    sub1 = "python string!"
    sub2 = "an arg"
    
    a = "i am a %s" % sub1
    b = "i am a {0}".format(sub1)
    
    c = "with %(kwarg)s!" % {'kwarg':sub2}
    d = "with {kwarg}!".format(kwarg=sub2)
    
    print a    # "i am a python string!"
    print b    # "i am a python string!"
    print c    # "with an arg!"
    print d    # "with an arg!"
    
  2. de plus, quand le formatage des chaînes se produit-il en Python? Par exemple, si mon journalisation le niveau est réglé à élevé est-ce que je vais quand même prendre un coup pour effectuer l'opération % suivante? Et si oui, est-il un moyen pour éviter cela?

    log.debug("some debug info: %s" % some_info)
    
1195
demandé sur the Tin Man 2011-02-22 21:46:42
la source

15 ответов

pour répondre à votre première question... .format semble juste plus sophistiqué à bien des égards. Une chose ennuyante au sujet de % est aussi comment il peut prendre une variable ou un tuple. On pourrait penser que ce qui suit marcherait toujours:

"hi there %s" % name

pourtant, si name se trouve être (1, 2, 3) , il lancera un TypeError . Pour garantir qu'il imprime toujours, vous devez faire

"hi there %s" % (name,)   # supply the single argument as a single-item tuple

qui est tout simplement laid. .format n'ont pas de ces questions. Dans le deuxième exemple que vous avez donné, l'exemple .format est beaucoup plus propre.

Pourquoi ne pas l'utiliser?

  • ne pas savoir à ce sujet (moi avant de lire cet)
  • devant être compatible avec Python 2.5

pour répondre à votre deuxième question, le formatage de chaîne de caractères se produit en même temps que toute autre opération - lorsque l'expression de formatage de chaîne est évaluée. Et Python, n'étant pas un langage paresseux, évalue les expressions avant d'appeler des fonctions, donc dans votre exemple log.debug , l'expression "some debug info: %s"%some_info sera d'abord évaluée à , par exemple "some debug info: roflcopters are active" , puis cette chaîne sera passée à log.debug() .

857
répondu Claudiu 2015-05-01 15:12:38
la source

quelque chose que l'opérateur modulo ( % ) ne peut pas faire, afaik:

tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)

résultat

12 22222 45 22222 103 22222 6 22222

très utile.

un Autre point: format() , étant une fonction, peut être utilisé comme argument dans d'autres fonctions:

li = [12,45,78,784,2,69,1254,4785,984]
print map('the number is {}'.format,li)   

print

from datetime import datetime,timedelta

once_upon_a_time = datetime(2010, 7, 1, 12, 0, 0)
delta = timedelta(days=13, hours=8,  minutes=20)

gen =(once_upon_a_time +x*delta for x in xrange(20))

print '\n'.join(map('{:%Y-%m-%d %H:%M:%S}'.format, gen))

résultats dans:

['the number is 12', 'the number is 45', 'the number is 78', 'the number is 784', 'the number is 2', 'the number is 69', 'the number is 1254', 'the number is 4785', 'the number is 984']

2010-07-01 12:00:00
2010-07-14 20:20:00
2010-07-28 04:40:00
2010-08-10 13:00:00
2010-08-23 21:20:00
2010-09-06 05:40:00
2010-09-19 14:00:00
2010-10-02 22:20:00
2010-10-16 06:40:00
2010-10-29 15:00:00
2010-11-11 23:20:00
2010-11-25 07:40:00
2010-12-08 16:00:00
2010-12-22 00:20:00
2011-01-04 08:40:00
2011-01-17 17:00:00
2011-01-31 01:20:00
2011-02-13 09:40:00
2011-02-26 18:00:00
2011-03-12 02:20:00
282
répondu eyquem 2015-01-14 22:24:14
la source

en supposant que vous utilisez le module logging de Python, vous pouvez passer les arguments de formatage de chaîne comme arguments à la méthode .debug() plutôt que de faire le formatage vous-même:

log.debug("some debug info: %s", some_info)

qui évite de formater à moins que le logger ne journalise quelque chose.

126
répondu Wooble 2015-01-14 22:28:08
la source

à partir de Python 3.6 (2016) vous pouvez utiliser F-strings pour remplacer les variables:

>>> origin = "London"
>>> destination = "Paris"
>>> f"from {origin} to {destination}"
'from London to Paris'

Note le préfixe f" . Si vous essayez cela en Python 3.5 ou plus tôt, vous obtiendrez un SyntaxError .

voir https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings

90
répondu Colonel Panic 2018-03-13 19:52:45
la source

PEP 3101 propose le remplacement de l'opérateur % par le nouveau formatage avancé de chaîne de caractères en Python 3, où ce serait la valeur par défaut.

54
répondu BrainStorm 2017-02-13 18:51:53
la source

mais soyez prudent, je viens de découvrir un problème en essayant de remplacer tout % par .format dans le code existant: '{}'.format(unicode_string) essaiera d'encoder unicode_string et échouera probablement.

regardez simplement ce journal de session interactif en Python:

Python 2.7.2 (default, Aug 27 2012, 19:52:55) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
; s='й'
; u=u'й'
; s
'\xd0\xb9'
; u
u'\u0439'

s est juste une chaîne (appelée 'byte array' en Python3) et u est une chaîne Unicode (appelée 'string' en Python3):

; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'

lorsque vous donnez un objet Unicode comme paramètre à l'opérateur % il produira une chaîne Unicode même si la chaîne originale n'était pas Unicode:

; '{}'.format(s)
'\xd0\xb9'
; '{}'.format(u)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u0439' in position 0: ordinal not in range(256)

mais la fonction .format relèvera "UnicodeEncodeError":

; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'

et il fonctionnera avec un argument Unicode amende que si la chaîne originale était Unicode.

; '{}'.format(u'i')
'i'

ou si chaîne de l'argument peut être converti en une chaîne (dénommé le "tableau d'octets')

51
répondu rslnx 2015-01-14 22:27:33
la source

encore un autre avantage de .format (que je ne vois pas dans les réponses): il peut prendre des propriétés d'objet.

In [12]: class A(object):
   ....:     def __init__(self, x, y):
   ....:         self.x = x
   ....:         self.y = y
   ....:         

In [13]: a = A(2,3)

In [14]: 'x is {0.x}, y is {0.y}'.format(a)
Out[14]: 'x is 2, y is 3'

Ou, comme un argument mot-clé:

In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'

ce n'est pas possible avec % autant que je peux dire.

33
répondu matiasg 2015-06-19 19:42:39
la source

comme je l'ai découvert aujourd'hui, l'ancienne façon de formater les chaînes via % ne supporte pas Decimal , le module de Python pour l'arithmétique décimale à virgule fixe et à virgule flottante, hors de la boîte.

exemple (utilisant Python 3.3.5):

#!/usr/bin/env python3

from decimal import *

getcontext().prec = 50
d = Decimal('3.12375239e-24') # no magic number, I rather produced it by banging my head on my keyboard

print('%.50f' % d)
print('{0:.50f}'.format(d))

sortie:

0.0000000000000000000003123752390000000000009907464850 0,000000000000000000000312375239000000000000000000000000

il pourrait sûrement y avoir des solutions de rechange, mais vous pourriez quand même envisager d'utiliser la méthode format() tout de suite.

27
répondu balu 2015-01-14 22:24:41
la source

% donne de meilleures performances que format de mon test.

code D'essai:

Python 2.7.2:

import timeit
print 'format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')")
print '%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')")

résultat:

> format: 0.470329046249
> %: 0.357107877731

Python 3.5.2

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))

résultat

> format: 0.5864730989560485
> %: 0.013593495357781649

il semble en Python2, la différence est faible alors qu'en Python3, % est beaucoup plus rapide que format .

Merci @Chris Cogdon pour le code échantillon.

23
répondu lcltj 2017-10-26 21:11:52
la source

comme note, vous n'avez pas besoin de prendre un coup de performance pour utiliser un nouveau style de formatage avec la journalisation. Vous pouvez passer n'importe quel objet à logging.debug , logging.info , etc. qui met en œuvre la méthode magique __str__ . Lorsque le module de journalisation a décidé qu'il doit émettre votre message (quel qu'il soit), il appelle str(message_object) avant de le faire. Donc, vous pourriez faire quelque chose comme ceci:

import logging


class NewStyleLogMessage(object):
    def __init__(self, message, *args, **kwargs):
        self.message = message
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        args = (i() if callable(i) else i for i in self.args)
        kwargs = dict((k, v() if callable(v) else v) for k, v in self.kwargs.items())

        return self.message.format(*args, **kwargs)

N = NewStyleLogMessage

# Neither one of these messages are formatted (or calculated) until they're
# needed

# Emits "Lazily formatted log entry: 123 foo" in log
logging.debug(N('Lazily formatted log entry: {0} {keyword}', 123, keyword='foo'))


def expensive_func():
    # Do something that takes a long time...
    return 'foo'

# Emits "Expensive log entry: foo" in log
logging.debug(N('Expensive log entry: {keyword}', keyword=expensive_func))

tout cela est décrit dans la documentation Python 3 ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles ). Cependant, il fonctionnera aussi avec Python 2.6 ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages ).

un des avantages de l'utilisation de cette technique, autre que le fait qu'elle est agnostique de formatage, est qu'elle permet des valeurs paresseuses par exemple la fonction expensive_func ci-dessus. Cela fournit une plus élégante alternative aux conseils donnés dans les docs Python ici: https://docs.python.org/2.6/library/logging.html#optimization .

14
répondu David Sanders 2014-10-21 23:55:49
la source

une situation où % peut aider est lorsque vous formatez des expressions regex. Par exemple,

'{type_names} [a-z]{2}'.format(type_names='triangle|square')

augmente IndexError . Dans cette situation, vous pouvez utiliser:

'%(type_names)s [a-z]{2}' % {'type_names': 'triangle|square'}

cela évite d'écrire le regex comme '{type_names} [a-z]{{2}}' . Cela peut être utile lorsque vous avez deux regexes, où l'un est utilisé seul sans format, mais la concaténation des deux est formaté.

8
répondu Jorge Leitão 2015-04-09 23:41:40
la source

pour la version python > = 3.6 (voir PEP 498 )

s1='albha'
s2='beta'

f'{s1}{s2:>10}'

#output
'albha      beta'
2
répondu Roushan 2018-03-15 19:52:14
la source

si votre python >= 3.6, F-string formaté littéral est votre nouvel ami.

C'est plus simple, propre, et de meilleures performances.

In [1]: params=['Hello', 'adam', 42]

In [2]: %timeit "%s %s, the answer to everything is %d."%(params[0],params[1],params[2])
448 ns ± 1.48 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit "{} {}, the answer to everything is {}.".format(*params)
449 ns ± 1.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit f"{params[0]} {params[1]}, the answer to everything is {params[2]}."
12.7 ns ± 0.0129 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
2
répondu zhengcao 2018-08-25 04:05:27
la source

j'ajouterais que depuis la version 3.6, nous pouvons utiliser des fstrings comme le suivant

foo = "john"
bar = "smith"
print(f"My name is {foo} {bar}")

qui donnent

mon nom est john smith

tout est converti en cordes

mylist = ["foo", "bar"]
print(f"mylist = {mylist}")

résultat:

maliste = ['foo', 'bar']

, vous pouvez passer fonction, comme dans d'autres formats méthode

print(f'Hello, here is the date : {time.strftime("%d/%m/%Y")}')

donnant par exemple

Bonjour, voici la date : 16/04/2018

1
répondu Sylvan LE DEUNFF 2018-04-16 17:51:11
la source

mais une chose est que même si vous avez emboîté broches, ne fonctionnera pas pour le format mais % fonctionnera.

exemple:

>>> '{{0}, {1}}'.format(1,2)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    '{{0}, {1}}'.format(1,2)
ValueError: Single '}' encountered in format string
>>> '{%s, %s}'%(1,2)
'{1, 2}'
>>> 
0
répondu U9-Forward 2018-08-25 04:08:57
la source