web-dev-qa-db-fra.com

Formatage de chaîne Python:% vs. .format

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

  1. Ce qui suit utilise chaque méthode et a 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 a-t-il lieu en Python? Par exemple, si mon niveau de journalisation est défini sur ÉLEVÉ, est-ce que je vais continuer à utiliser pour exécuter l'opération % suivante? Et si oui, y a-t-il un moyen d'éviter cela?

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

Pour répondre à votre première question ... .format semble plus sophistiqué à bien des égards. Une chose ennuyeuse à propos de % est aussi comment il peut prendre une variable ou un tuple. Vous penseriez que ce qui suit fonctionnerait toujours:

"hi there %s" % name

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

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

ce qui est juste moche. .format n'a pas ces problèmes. Également dans le deuxième exemple que vous avez donné, l'exemple .format est beaucoup plus propre.

Pourquoi ne l'utiliseriez-vous pas? 

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

Pour répondre à votre deuxième question, le formatage de chaîne a lieu 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. Ainsi, 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", cette chaîne sera transmise à log.debug()

888
Claudiu

Quelque chose que l'opérateur de modulo (%) ne peut pas faire, autant que je sache:

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ésulte en:

['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
289
eyquem

En supposant que vous utilisiez le module logging de Python, vous pouvez transmettre les arguments de formatage de chaîne comme arguments à la méthode .debug() plutôt que de procéder vous-même au formatage:

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

ce qui évite de faire le formatage à moins que l'enregistreur enregistre quelque chose.

132
Wooble

À partir de Python 3.6 (2016), vous pouvez utiliser f-strings pour substituer des variables:

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

Notez le préfixe f". Si vous essayez ceci avec Python 3.5 ou une version antérieure, vous obtiendrez une SyntaxError.

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

98
Colonel Panic

PEP 3101 propose le remplacement de l'opérateur % par le nouveau formatage de chaîne avancé en Python 3, où il s'agirait du comportement par défaut.

55
BrainStorm

Mais s'il vous plaît 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 de coder unicode_string et échouera probablement.

Il suffit de regarder ce journal de session interactif 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 'tableau d'octets' en Python3) et u est une chaîne Unicode (appelée 'chaîne' en Python3):

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

Lorsque vous donnez un objet Unicode en tant que paramètre à l'opérateur %, il produira une chaîne Unicode, même si la chaîne d'origine 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 lèvera "UnicodeEncodeError":

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

et cela fonctionnera avec un argument Unicode très bien seulement si la chaîne originale était Unicode.

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

ou si la chaîne d'argument peut être convertie en chaîne (appelée "tableau d'octets")

51
rslnx

Encore un autre avantage de .format (que je ne vois pas dans les réponses): cela 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, en tant qu'argument de mot clé:

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

Autant que je sache, ceci n’est pas possible avec %.

33
matiasg

Comme je l’ai découvert aujourd’hui, l’ancienne méthode de formatage des chaînes via % ne prend pas en charge Decimal, le module Python pour l’arithmétique décimale à virgule fixe et à virgule flottante.

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.0000000000000000000000300007533975000000009907464850 0.00000000000000000000003000312375239000000000000000000

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

28
balu

% donne de meilleures performances que format de mon test.

Code de test:

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

Cela ressemble à Python2, la différence est petite, alors que dans Python3, % est beaucoup plus rapide que format.

Merci @Chris Cogdon pour l'exemple de code.

25
lcltj

En guise de remarque, il n'est pas nécessaire de perdre des performances pour utiliser un nouveau formatage de style avec la journalisation. Vous pouvez transmettre n'importe quel objet à logging.debug, logging.info, etc. implémentant la méthode magique __str__. Lorsque le module de journalisation a décidé qu'il doit émettre votre objet de message (quel qu'il soit), il appelle str(message_object) avant de le faire. Donc, vous pourriez faire quelque chose comme ça:

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 de Python 3 ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles ). Cependant, cela fonctionnera également avec Python 2.6 ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages ).

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

14
David Sanders

% peut vous aider lorsque vous formatez des expressions regex. Par exemple, 

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

soulève IndexError. Dans cette situation, vous pouvez utiliser:

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

Cela évite d’écrire le regex avec '{type_names} [a-z]{{2}}'. Cela peut être utile lorsque vous avez deux expressions rationnelles, où l'une est utilisée seule sans format, mais la concaténation des deux est formatée.

8
Jorge Leitão

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

C'est plus simple, plus 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)
6
zhengcao

J'ajouterais que depuis la version 3.6, on peut utiliser des fstrings comme celui-ci

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

Qui donnent

Je m'appelle john smith

Tout est converti en chaînes

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

Résultat:

mylist = ['foo', 'bar']

vous pouvez passer la fonction, comme dans la méthode des autres formats

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

Donner par exemple

Bonjour, voici la date: 16/04/2018

4
Sylvan LE DEUNFF

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

s1='albha'
s2='beta'

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

#output
'albha      beta'
2
Roushan

Comparatif Python 3.6.7:

#!/usr/bin/env python
import timeit

def time_it(fn):
    """
    Measure time of execution of a function
    """
    def wrapper(*args, **kwargs):
        t0 = timeit.default_timer()
        fn(*args, **kwargs)
        t1 = timeit.default_timer()
        print("{0:.10f} seconds".format(t1 - t0))
    return wrapper


@time_it
def new_new_format(s):
    print("new_new_format:", f"{s[0]} {s[1]} {s[2]} {s[3]} {s[4]}")


@time_it
def new_format(s):
    print("new_format:", "{0} {1} {2} {3} {4}".format(*s))


@time_it
def old_format(s):
    print("old_format:", "%s %s %s %s %s" % s)


def main():
    samples = (("uno", "dos", "tres", "cuatro", "cinco"), (1,2,3,4,5), (1.1, 2.1, 3.1, 4.1, 5.1), ("uno", 2, 3.14, "cuatro", 5.5),) 
    for s in samples:
        new_new_format(s)
        new_format(s)
        old_format(s)
        print("-----")


if __== '__main__':
    main()

Sortie:

new_new_format: uno dos tres cuatro cinco
0.0000170280 seconds
new_format: uno dos tres cuatro cinco
0.0000046750 seconds
old_format: uno dos tres cuatro cinco
0.0000034820 seconds
-----
new_new_format: 1 2 3 4 5
0.0000043980 seconds
new_format: 1 2 3 4 5
0.0000062590 seconds
old_format: 1 2 3 4 5
0.0000041730 seconds
-----
new_new_format: 1.1 2.1 3.1 4.1 5.1
0.0000092650 seconds
new_format: 1.1 2.1 3.1 4.1 5.1
0.0000055340 seconds
old_format: 1.1 2.1 3.1 4.1 5.1
0.0000052130 seconds
-----
new_new_format: uno 2 3.14 cuatro 5.5
0.0000053380 seconds
new_format: uno 2 3.14 cuatro 5.5
0.0000047570 seconds
old_format: uno 2 3.14 cuatro 5.5
0.0000045320 seconds
-----
1
Felix Martinez

Mais une chose est que même si vous avez des accolades imbriquées, cela 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}'
>>> 
1
U9-Forward

Strictement vu, nous nous éloignons vraiment du sujet initial, mais alors pourquoi pas: 

Lorsque vous utilisez le module gettext pour fournir, par exemple, une interface graphique localisée, des chaînes de style anciennes et nouvelles sont le seul moyen; Les f-strings ne peuvent pas être utilisés ici. IMHO le nouveau style sont le meilleur choix pour ce cas. Il y a une SO question à ce sujet ici .

0
jake77