Python 2.6 introdujo el str.format()
método con una sintaxis ligeramente diferente del %
operador existente . ¿Cuál es mejor y para qué situaciones?
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!"
Además, ¿cuándo se produce el formateo de cadenas en Python? Por ejemplo, si mi nivel de registro está establecido en ALTO, ¿recibiré un golpe por realizar la siguiente
%
operación? Y si es así, ¿hay alguna manera de evitar esto?log.debug("some debug info: %s" % some_info)
python
performance
logging
string-formatting
f-string
NorthIsUp
fuente
fuente
%
estilo más antiguo con más frecuencia, porque si no necesita las capacidades mejoradas delformat()
estilo, el%
estilo suele ser mucho más conveniente.format()
estilo de formato y el mayor%
estilo de formato basado .Respuestas:
Para responder a su primera pregunta ...
.format
simplemente parece más sofisticado en muchos sentidos. Una cosa molesta%
es también cómo puede tomar una variable o una tupla. Uno pensaría que lo siguiente siempre funcionaría:sin embargo, si
name
es así(1, 2, 3)
, arrojará unTypeError
. Para garantizar que siempre se imprima, deberías hacerlo cual es feo
.format
No tiene esos problemas. También en el segundo ejemplo que diste, el.format
ejemplo tiene un aspecto mucho más limpio.¿Por qué no lo usarías?
Para responder a su segunda pregunta, el formateo de cadenas ocurre al mismo tiempo que cualquier otra operación, cuando se evalúa la expresión de formateo de cadenas. Y Python, al no ser un lenguaje vago, evalúa las expresiones antes de llamar a las funciones, por lo que en su
log.debug
ejemplo, la expresión"some debug info: %s"%some_info
primero evaluará, por ejemplo"some debug info: roflcopters are active"
, luego se pasará esa cadenalog.debug()
.fuente
"%(a)s, %(a)s" % {'a':'test'}
log.debug("something: %s" % x)
pero no paralog.debug("something: %s", x)
El formato de cadena se manejará en el método y no obtendrá el impacto en el rendimiento si no se registra. Como siempre, Python anticipa tus necesidades =)'{0}, {0}'.format('test')
.man sprintf
y aprende sobre la$
notación dentro de los%
marcadores de posiciónprintf("%2$d", 1, 3)
imprimir "3", eso se especifica en POSIX, no en C99. La misma página de manual a la que hizo referencia señala: "El estándar C99 no incluye el estilo con '$' ...".Algo que el operador de módulo (%) no puede hacer, afaik:
resultado
Muy útil.
Otro punto: al
format()
ser una función, puede usarse como argumento en otras funciones:Resultados en:
fuente
map
misma facilidad que el formato.map('some_format_string_%s'.__mod__, some_iterable)
printf("%2$s %1$s\n", "One", "Two");
compilado congcc -std=c99 test.c -o test
, la salida esTwo One
. Pero estoy corregido: en realidad es una extensión POSIX y no C. No puedo encontrarlo nuevamente en el estándar C / C ++, donde pensé que lo había visto. El código funciona incluso con la bandera estándar 'c90'.sprintf
página man . Esto no lo enumera, pero permite que las bibliotecas implementen un superconjunto. Mi argumento original sigue siendo válido, reemplazandoC
conPosix
%
para reordenar marcadores de posición. Todavía me gustaría no eliminar ese primer comentario en aras de la coherencia de los comentarios aquí. Pido disculpas por haber desahogado mi ira aquí. Está dirigido contra la afirmación hecha a menudo de que la sintaxis anterior per se no permitiría esto. En lugar de crear una sintaxis completamente nueva, podríamos haber introducido las extensiones std Posix. Podríamos tener ambos.Suponiendo que está utilizando el
logging
módulo de Python , puede pasar los argumentos de formato de cadena como argumentos al.debug()
método en lugar de hacerlo usted mismo:lo que evita hacer el formateo a menos que el registrador realmente registre algo.
fuente
log.debug("some debug info: %(this)s and %(that)s", dict(this='Tom', that='Jerry'))
Sin embargo, no puede usar la nueva.format()
sintaxis de estilo aquí, ni siquiera en Python 3.3, lo cual es una pena.A partir de Python 3.6 (2016) puede usar cadenas f para sustituir variables:
Tenga en cuenta el
f"
prefijo. Si intenta esto en Python 3.5 o anterior, obtendrá unSyntaxError
.Ver https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings
fuente
PEP 3101 propone el reemplazo del
%
operador con el nuevo formato de cadena avanzado en Python 3, donde sería el predeterminado.fuente
.format
no reemplazará el%
formato de cadena.Pero tenga cuidado, justo ahora descubrí un problema al intentar reemplazar todo
%
con el.format
código existente:'{}'.format(unicode_string)
intentará codificar unicode_string y probablemente fallará.Solo mira este registro de sesión interactiva de Python:
s
es solo una cadena (llamada 'matriz de bytes' en Python3) yu
es una cadena Unicode (llamada 'cadena' en Python3):Cuando le das un objeto Unicode como parámetro al
%
operador, producirá una cadena Unicode incluso si la cadena original no era Unicode:pero la
.format
función generará "UnicodeEncodeError":y funcionará con un argumento Unicode bien solo si la cadena original era Unicode.
o si la cadena de argumento se puede convertir en una cadena (llamada 'matriz de bytes')
fuente
format
método sean realmente necesarias ...%
interpolación de cadenas desaparezca."p1=%s p2=%d" % "abc", 2
o"p1=%s p2=%s" % (tuple_p1_p2,)
. Puede pensar que es culpa del codificador, pero creo que es una sintaxis defectuosa extraña que se ve bien para la escritura rápida pero es mala para el código de producción.%s
,%02d
como"p1=%s p2=%02d".format("abc", 2)
. Culpo a los que inventaron y aprobaron el formato de llaves que necesita que escapen{{}}
y se ve feo en mi humilde opinión.Otra ventaja más
.format
(que no veo en las respuestas): puede tomar propiedades de objeto.O, como argumento de palabra clave:
Esto no es posible con
%
lo que puedo decir.fuente
'x is {0}, y is {1}'.format(a.x, a.y)
. Solo debe usarse cuando laa.x
operación es muy costosa.'x is {a.x}, y is {a.y}'.format(a=a)
. Más legible que ambos ejemplos.'x is {a.x}, y is {a.y}'.format(**vars())
'{foo[bar]}'.format(foo={'bar': 'baz'})
.Your order, number {order[number]} was processed at {now:%Y-%m-%d %H:%M:%S}, will be ready at about {order[eta]:%H:%M:%S}
o lo que desee. Esto es mucho más limpio que tratar de ofrecer la misma funcionalidad con el antiguo formateador. Hace que las cadenas de formato proporcionadas por el usuario sean mucho más potentes.%
da un mejor rendimiento que elformat
de mi prueba.Código de prueba:
Python 2.7.2:
Resultado:
Python 3.5.2
Resultado
Se ve en Python2, la diferencia es pequeña, mientras que en Python3,
%
es mucho más rápido queformat
.Gracias @Chris Cogdon por el código de muestra.
Editar 1:
Probado nuevamente en Python 3.7.2 en julio de 2019.
Resultado:
No hay mucha diferencia Supongo que Python está mejorando gradualmente.
Edición 2:
Después de que alguien mencionó la cadena f de python 3 en el comentario, hice una prueba para el siguiente código en python 3.7.2:
Resultado:
Parece que f-string es aún más lento
%
pero mejor queformat
.fuente
str.format
ofrece más funcionalidades (especialmente formato de tipo especializado, por ejemplo'{0:%Y-%m-%d}'.format(datetime.datetime.utcnow())
). El rendimiento no puede ser el requisito absoluto de todos los trabajos. Use la herramienta adecuada para el trabajo.%
operador permite reutilizar elprintf
conocimiento; La interpolación del diccionario es una extensión muy simple del principio.Como descubrí hoy, la antigua forma de formatear cadenas a través de
%
no es compatibleDecimal
, el módulo de Python para punto fijo decimal y aritmética de punto flotante, fuera de la caja.Ejemplo (usando Python 3.3.5):
Salida:
Seguramente puede haber soluciones, pero aún puede considerar usar el
format()
método de inmediato.fuente
str(d)
antes de expandir el parámetro, mientras que el formato de estilo antiguo probablemente llamafloat(d)
primero.str(d)
regresa"3.12375239e-24"
, no"0.00000000000000000000000312375239000000000000000000"
Si su python> = 3.6, el literal con formato de F-string es su nuevo amigo.
Es más simple, limpio y mejor rendimiento.
fuente
Como nota al margen, no tiene que tomar un golpe de rendimiento para usar el nuevo formato de estilo con el registro. Puede pasar cualquier objeto a
logging.debug
,logging.info
, etc., que implementa el__str__
método mágico. Cuando el módulo de registro ha decidido que debe emitir su objeto de mensaje (sea lo que sea), llamastr(message_object)
antes de hacerlo. Entonces podrías hacer algo como esto: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, además del hecho de que es independiente del estilo de formato, es que permite valores vagos, por ejemplo, la función
expensive_func
anterior. 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 .fuente
format
sin el impacto en el rendimiento, lo hace al anular__str__
precisamente comologging
se diseñó, acorta la llamada de función a una sola letra (N
) que se siente muy similar a algunas de las formas estándar de definir cadenas, Y permite la pereza función de llamada. ¡Gracias! +1logging.Formatter(style='{')
parámetro?Una situación en la que
%
puede ayudar es cuando está formateando expresiones regex. Por ejemplo,plantea
IndexError
. En esta situación, puede usar:Esto evita escribir la expresión regular como
'{type_names} [a-z]{{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.fuente
'{type_names} [a-z]{{2}}'.format(type_names='triangle|square')
. Es como decir que.format()
puede ayudar cuando se usan cadenas que ya contienen un carácter de porcentaje. Por supuesto. Tienes que escapar de ellos entonces."One situation where % may help is when you are formatting regex expressions."
Específicamente, suponga quea=r"[a-z]{2}"
es un fragmento de expresión regular que se utilizará en dos expresiones finales diferentes (por ejemplo,c1 = b + a
yc2 = a
). Suponga quec1
necesita serformat
editado (por ejemplo,b
debe estar formateado en tiempo de ejecución), peroc2
no es así. Entonces necesitasa=r"[a-z]{2}"
porc2
ya=r"[a-z]{{2}}"
parac1.format(...)
.Agregaría que desde la versión 3.6, podemos usar cadenas f como la siguiente
Que dan
Todo se convierte en cuerdas.
Resultado:
puede pasar la función, como en el método de otros formatos
Dando por ejemplo
fuente
Para la versión de Python> = 3.6 (ver PEP 498 )
fuente
Python 3.6.7 comparativo:
Salida:
fuente
Pero una cosa es que también si ha anidado llaves, no funcionará para el formato, pero
%
funcionará.Ejemplo:
fuente