practices - python logging formatter example




Formatage de chaîne Python:% par rapport à.format (9)

À 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 dans Python 3.5 ou plus tôt, vous obtiendrez une SyntaxError .

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

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 la mise en forme des chaînes se produit-elle en Python? Par exemple, si mon niveau de journalisation est défini sur HAUT, est-ce que je vais quand même effectuer un hit pour effectuer l'opération % suivante? Et si oui, y a-t-il un moyen d'éviter cela?

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

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 de Python pour le point fixe décimal et l'arithmétique à virgule flottante, prêt à l'emploi.

Exemple (en 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,00000000000000000000000312375239000000009907464850 0.00000000000000000000000312375239000000000000000000

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


En supposant que vous utilisez le module de logging de Python, vous pouvez passer les arguments de mise en forme de chaîne en tant qu'arguments à la méthode .debug() plutôt qu'en effectuant la mise en forme vous-même:

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

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


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, en tant qu'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 sache.


Mais s'il vous plaît soyez prudent, tout à l'heure j'ai découvert un problème en essayant de remplacer tous les % avec .format dans le code existant: '{}'.format(unicode_string) va essayer d'encoder unicode_string et échouera probablement.

Regardez ce journal de session interactive 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' dans Python3) et u est une chaîne Unicode (appelée 'string' dans Python3):

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

Lorsque vous donnez un objet Unicode en tant que paramètre à l'opérateur % , il produit 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 déclenchera "UnicodeEncodeError":

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

et cela fonctionnera avec un argument Unicode uniquement si la chaîne d'origine était Unicode.

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

ou si la chaîne d'argument peut être convertie en chaîne (appelée 'byte array')


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

s1='albha'
s2='beta'

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

#output
'albha      beta'

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

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 la regex sous la forme '{type_names} [az]{{2}}' . Cela peut être utile lorsque vous avez deux regex, où l'un est utilisé seul sans format, mais la concaténation des deux est formatée.


% donne de meilleures performances que le 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

Il regarde dans Python2, la différence est petite alors que dans Python3, % est beaucoup plus rapide que le format .

Merci @Chris Cogdon pour l'exemple de code.







string-formatting