values tutorial logger log disable_existing_loggers python performance logging string-formatting

tutorial - python logging disable_existing_loggers



Formato de cadena de Python:% vs..format (15)

Python 2.6 introdujo el método str.format() con una sintaxis ligeramente diferente del operador % existente. ¿Cuál es mejor y para qué situaciones?

  1. Lo siguiente usa cada método y tiene el mismo resultado, entonces, ¿cuál es la diferencia?

    #!/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. Además, ¿cuándo se produce el formato de cadena en Python? Por ejemplo, si mi nivel de registro está establecido en ALTO, ¿seguiré teniendo éxito para realizar la siguiente operación % ? Y si es así, ¿hay alguna manera de evitar esto?

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



Algo que el operador de módulo (%) no puede hacer, afaik:

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

resultado

12 22222 45 22222 103 22222 6 22222

Muy útil.

Otro punto: el format() , al ser una función, se puede usar como un argumento en otras funciones:

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

Resultados 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


Como descubrí hoy, la antigua forma de formatear cadenas a través de % no es compatible con Decimal , el módulo de Python para aritmética de coma fija y coma flotante decimal.

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

Salida:

0.00000000000000000000000312375239000000009907464850 0.0000000000000000000003123752390000000000000000

Es probable que existan soluciones alternativas, pero aún puede considerar el uso del método format() inmediato.


Como nota al margen, no tiene que dar un golpe de rendimiento para usar un nuevo formato de estilo con el registro. Puede pasar cualquier objeto a logging.debug , logging.info , etc. que implemente el método mágico __str__ . Cuando el módulo de registro ha decidido que debe emitir su objeto de mensaje (sea el que sea), llama a str(message_object) antes de hacerlo. Así que podrías hacer algo como esto:

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

Todo esto se describe en la documentación de Python 3 ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles ). Sin embargo, también funcionará con Python 2.6 ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages ).

Una de las ventajas de usar esta técnica, aparte del hecho de que es agnóstico al estilo de formato, es que permite valores perezosos, por ejemplo, la función expensive_func arriba. Esto proporciona una alternativa más elegante a los consejos que se dan en los documentos de Python aquí: https://docs.python.org/2.6/library/logging.html#optimization .


Otra ventaja más de .format (que no veo en las respuestas): puede tomar propiedades de objeto.

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

O, como argumento de palabra clave:

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

Esto no es posible con % por lo que puedo decir.


Para la versión de python> = 3.6 (ver PEP 498 )

s1=''albha'' s2=''beta'' f''{s1}{s2:>10}'' #output ''albha beta''


Para responder a su primera pregunta ... .format simplemente parece más sofisticado de muchas maneras. Una cosa molesta acerca de % también es cómo puede tomar una variable o una tupla. Usted pensaría que lo siguiente siempre funcionaría:

"hi there %s" % name

sin embargo, si el name es (1, 2, 3) , lanzará un TypeError . Para garantizar que siempre se imprima, tendrías que hacerlo.

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

que es simplemente feo .format no tiene esos problemas. También en el segundo ejemplo que dio, el ejemplo de .format es mucho más limpio.

¿Por qué no lo usarías?

  • Sin saberlo (yo antes de leer esto)
  • Tener que ser compatible con Python 2.5.

Para responder a su segunda pregunta, el formato de cadena ocurre al mismo tiempo que cualquier otra operación, cuando se evalúa la expresión de formato de cadena. Y Python, al no ser un lenguaje perezoso, evalúa las expresiones antes de llamar a las funciones, por lo que en su ejemplo log.debug , la expresión "some debug info: %s"%some_info se evaluará primero, por ejemplo, "some debug info: roflcopters are active" , entonces esa cadena se pasará a log.debug() .


Pero tenga cuidado, ahora mismo he descubierto un problema al intentar reemplazar todo % con .format en el código existente: ''{}''.format(unicode_string) intentará codificar unicode_string y probablemente fallará.

Basta con mirar este registro de sesión interactivo de 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 es solo una cadena (llamada ''matriz de bytes'' en Python3) y u es una cadena Unicode (llamada ''cadena'' en Python3):

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

Cuando le das un objeto Unicode como parámetro al operador % , producirá una cadena Unicode incluso si la cadena original no era 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)

pero la función .format "UnicodeEncodeError":

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

y funcionará con un argumento Unicode bien solo si la cadena original era Unicode.

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

o si la cadena de argumento se puede convertir en una cadena (llamada ''matriz de bytes'')


Pero una cosa es que, además, si tiene anidadas llaves, no funcionará para el formato pero el % funcionará

Ejemplo:

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


Si tu python> = 3.6, F-string formateado literal es tu nuevo amigo.

Es más sencillo, limpio, y con mejor rendimiento.

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)


Suponiendo que está utilizando el módulo de logging de Python, puede pasar los argumentos de formato de cadena como argumentos al método .debug() lugar de hacer el formato usted mismo:

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

que evita realizar el formateo a menos que el registrador realmente registre algo.


Una situación en la que % puede ayudar es cuando formatea expresiones de expresiones regulares. Por ejemplo,

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

plantea IndexError . En esta situación, puede utilizar:

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

Esto evita escribir la expresión regular como ''{type_names} [az]{{2}}'' . Esto puede ser útil cuando tiene dos expresiones regulares, donde una se usa sola sin formato, pero la concatenación de ambas está formateada.


Yo agregaría que desde la versión 3.6, podemos usar fstrings como los siguientes

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

Que dan

Mi nombre es john smith

Todo se convierte en cuerdas.

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

Resultado:

mylist = [''foo'', ''bar'']

Puede pasar la función, como en otros formatos del método.

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

Dando por ejemplo

Hola, aquí está la fecha: 16/04/2018.


PEP 3101 propone la sustitución del operador % con el nuevo formato de cadena avanzado en Python 3, donde sería el predeterminado.


% da mejor rendimiento que el format de mi prueba.

Código de prueba:

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

Resultado:

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

Resultado

> format: 0.5864730989560485 > %: 0.013593495357781649

Se ve en Python2, la diferencia es pequeña, mientras que en Python3, % es mucho más rápido que el format .

Gracias a @Chris Cogdon por el código de muestra.