88 Palabras clave de la programación eficiente en Python (2): ¿Realmente se pueden formatear cadenas?

Tabla de contenido

1. Formato de cadena de estilo C

2. Función de formato incorporada y método str.format

3. cuerda f

para resumir:

 

Ingrese 595586 en la cuenta pública de WeChat "Geek Origin" para conocer todos los artículos de la serie "88 captaciones para una programación eficiente en Python".

En el lenguaje Python, las cadenas tienen muchos usos. Se puede usar para mostrar mensajes en la interfaz de usuario y las utilidades de la línea de comandos; se usa para escribir datos en archivos y sockets; se usa para especificar mensajes "anormales"; se usa para depurar programas.

El formateo es el proceso de combinar texto y datos predefinidos en un mensaje legible por humanos. Python tiene 4 formas diferentes de formatear cadenas. Algunas de estas 4 formas son compatibles a nivel de lenguaje y otras son compatibles con la biblioteca estándar. A excepción de uno de ellos, otros métodos de formateo tienen serias deficiencias, que deben evitarse al usarlos.

1. Formato de cadena de estilo C

La forma más común de formatear cadenas en el lenguaje Python es usar el operador de formato%. La plantilla de texto predefinida se coloca en el lado izquierdo del operador% en forma de una cadena de formato, y los datos que se insertarán en la plantilla están en el lado derecho del operador%. Estos datos pueden ser un valor único o una tupla (no una lista), lo que significa insertar varios valores en la plantilla. Por ejemplo, aquí utilizo el operador% para convertir valores binarios y hexadecimales difíciles de leer en cadenas enteras:

a = 0b10111010
b = 0xc5c
print('二进制:%d, 十六进程:%d' % (a, b))

La ejecución de este código generará lo siguiente:

二进制:186, 十六进程:3164

La cadena de formato usa especificadores de formato (como% d) como marcadores de posición, que serán reemplazados por los valores en el lado derecho del operador%. La sintaxis del especificador de formato proviene de la función printf del lenguaje C, que ha sido heredada por Python (y otros lenguajes de programación). Python admite todas las opciones de formato de la función printf de uso común. Como especificadores de formato% s,% x y% f, así como el control de posiciones decimales, relleno, relleno y alineación. Muchos programadores que no están familiarizados con Python comienzan con cadenas de formato de estilo C porque están familiarizados y son fáciles de usar.

Sin embargo, el uso de métodos de cadena de formato de estilo C traerá los siguientes cuatro problemas:

Pregunta 1:

Si cambia el tipo o el orden de los valores de datos en la tupla en el lado derecho de la expresión de formato, se puede producir una excepción debido a la conversión de tipos incompatibles. Por ejemplo, esta expresión de formato simple puede funcionar:

key = 'my_key'
value = 1.234
formatted = '%-10s = %.2f' % (key, value)
print(formatted)

La ejecución de este código generará lo siguiente:

my_key     = 1.23

Pero cómo intercambiar los valores de clave y valor, arrojará una excepción de tiempo de ejecución:

key = 1.234
value = 'my_key'
formatted = '%-10s = %.2f' % (key, value)
print(formatted)

La ejecución de este código arrojará la siguiente excepción:

Traceback (most recent call last):
  File "/python/format.py", line 12, in <module>
    formatted = '%-10s = %.2f' % (key, value)
TypeError: must be real number, not str

De manera similar, si cambia el orden de los valores en la tupla a la derecha de%, también se lanzará una excepción.

formatted = '%-10s = %.2f' % (key, value)

Para evitar este problema, debe verificar constantemente si los tipos de datos en ambos lados del operador% coinciden; este proceso es propenso a errores, porque cada vez que modifica el código, debe verificar manualmente si los tipos de datos coinciden.

Pregunta 2:

El segundo problema con las expresiones de formato de estilo C es que cuando necesita realizar pequeños cambios en el valor antes de formatearlo como una cadena, se volverán difíciles de leer, lo cual es un requisito muy común. Aquí, enumeré el contenido de la despensa de la cocina sin hacer cambios en línea:

pantry = [
    ('avocados', 1.25),
    ('bananas', 2.5),
    ('cherries', 15),
]
for i, (item, count) in enumerate(pantry):
    print('#%d: %-10s = %.2f' % (i, item, count))

La ejecución de este código generará los siguientes resultados:

#0: avocados   = 1.25
#1: bananas    = 2.50
#2: cherries   = 15.00

Ahora, he realizado algunos cambios en el valor que se va a formatear para poder imprimir información más útil. Esto hace que la tupla en la expresión de formato sea demasiado larga, por lo que debe dividirse en varias líneas, lo que dañará la legibilidad del programa:

for i, (item, count) in enumerate(pantry):
    print('#%d: %-10s = %d' % (
        i + 1,
        item.title(),
        round(count)))

La ejecución de este código generará el siguiente contenido:

#1: Avocados   = 1
#2: Bananas    = 2
#3: Cherries   = 15

Pregunta 3:

El tercer problema con las expresiones de formato es que si desea utilizar el mismo valor varias veces en la cadena de formato, debe repetir el valor varias veces en el lado derecho:

template = '%s loves food. See %s cook.'
name = 'Max'
formatted = template % (name, name)
print(formatted)

La ejecución de este código generará el siguiente contenido:

Max loves food. See Max cook.

Si necesita hacer algunos pequeños cambios en estos valores repetidos, esto será particularmente molesto y muy propenso a errores. Para resolver este problema, se recomienda utilizar un diccionario en lugar de tuplas para proporcionar datos para la cadena de formato. La forma de referirse al valor en el diccionario es% (clave), vea el siguiente ejemplo:

old_way = '%-10s , %.2f, %-8s' % (key, value,key)  # 重复指定key


new_way = '%(key)-10s , %(value).2f, %(key)-8s' % {
            'key': key, 'value': value}            # 只需要指定一次key 


print(old_way)
print(new_way)

La ejecución de este código generará el siguiente contenido:

key1       , 1.13, key1    
key1       , 1.13, key1

Podemos ver que si los valores en el lado derecho de% necesitan ser citados repetidamente, en el caso de usar tuplas, estos valores deben especificarse repetidamente, como la clave en este ejemplo. Para usar un diccionario, solo necesita especificar la clave una vez.

Luego, usar un diccionario para formatear cadenas puede introducir y exacerbar otros problemas. Para la pregunta 2 anterior, debido a la pequeña modificación del valor antes del formateo, la expresión de formato se vuelve más larga y visualmente más desordenada debido a la presencia del operador de clave y dos puntos en el lado derecho del operador%. En el siguiente código, uso un diccionario y no uso punteros para formatear la misma cadena para ilustrar este problema:

for i, (item, count) in enumerate(pantry):
    before = '#%d: %-10s = %d' % (
        i + 1,
        item.title(),
        round(count))


    after = '#%(loop)d: %(item)-10s = %(count)d' % {
        'loop': i + 1,
        'item': item.title(),
        'count': round(count),
    }


    assert before == after

Pregunta 4:

El uso de cadenas de formato de diccionario también trae el cuarto problema, es decir, cada clave debe especificarse al menos dos veces: una vez en el especificador de formato y la otra vez en el diccionario como clave. Si el valor del diccionario en sí es una variable También es necesario especificarlo de nuevo.

soup = 'lentil'
formatted = 'Today\'s soup is %(soup)s.' % {'soup': soup}   # 这里再次指定了变量soup
print(formatted)

El resultado es el siguiente:

Today's soup is lentil.

Además de los caracteres repetidos, esta redundancia también puede dar lugar a expresiones de formato largas utilizando diccionarios. Estas expresiones generalmente deben abarcar varias líneas, la cadena de formato está concatenada en varias líneas y la asignación del diccionario solo tiene una línea para cada valor de formato:

menu = {
    'soup': 'lentil',
    'oyster': 'kumamoto',
    'special': 'schnitzel',
}
template = ('Today\'s soup is %(soup)s, '
            'buy one get two %(oyster)s oysters, '
            'and our special entrée is %(special)s.')
formatted = template % menu
print(formatted)

El resultado es el siguiente:

Today's soup is lentil, buy one get two kumamoto oysters, and our special entrée is schnitzel.

Debido a que la cadena de formato es muy larga y puede abarcar varias líneas, para comprender qué quiere expresar toda la cadena, sus gafas deben moverse hacia arriba y hacia abajo, hacia la izquierda y hacia la derecha, y es fácil ignorar los errores que deberían haberse encontrado. Entonces, ¿hay una mejor solución para formatear cadenas? por favor observe la siguiente parte:

2. Función de formato incorporada y método str.format

Python 3 agrega soporte para el formato de cadena avanzado, que es más expresivo que las cadenas con formato de estilo C utilizando el operador%. Para valores individuales, puede acceder a esta nueva función formateando la función incorporada. Por ejemplo, el siguiente código usa algunas opciones nuevas (, para el separador de milésimas y ^ para centrar) para formatear el valor:

a = 1234.5678
formatted = format(a, ',.2f')
print(formatted)


b = 'my string'
formatted = format(b, '^20s')    # 居中显示字符串
print('*', formatted, '*')

Los resultados son los siguientes:

1,234.57
*      my string       *

Puede formatear varios valores llamando al método de formato de la cadena. El método de formato usa {} como marcador de posición en lugar de usar un especificador de formato de estilo C como% d. De forma predeterminada, los marcadores de posición en la cadena de formato se pasan a los marcadores de posición en la posición correspondiente del método de formato en el orden en que aparecen.

key = 'my_var'
value = 1.234


formatted = '{} = {}'.format(key, value)
print(formatted)

Los resultados son los siguientes:

my_var = 1.234

En cada marcador de posición, puede especificar un especificador de formato después de los dos puntos (:) para especificar la forma de convertir el valor en una cadena. El código es el siguiente:

formatted = '{:<10} = {:.2f}'.format(key, value)
print(formatted)

Los resultados son los siguientes:

my_var      = 1.23

El principio de funcionamiento del método de formato es pasar el especificador de formato y el valor (formato (valor, '. 2f') en el ejemplo anterior) al formato de la función incorporada. Luego reemplace el marcador de posición correspondiente con el valor de retorno de la función. Puede utilizar el método __format__ para personalizar el comportamiento de formato de cada clase.

Para cadenas de formato de estilo C, el operador% debe convertirse y escaparse, es decir, escribir dos%, para no confundirse con marcadores de posición. Usando el método str.format, también necesita escapar de las llaves.

print('%.2f%%' % 12.5)
print('{} replaces {
    
    {}}'.format(1.23))

El resultado es el siguiente:

12.50%
1.23 replaces {}

Dentro de las llaves, también puede especificar el índice de posición de los parámetros pasados ​​al método de formato para reemplazar los marcadores de posición. Esto permite cambiar el orden de los marcadores de posición en la cadena de formato sin cambiar el orden de los valores pasados ​​en el método de formato.

formatted = '{1} = {0}'.format(key, value)
print(formatted)

El resultado es el siguiente:

1.234 = my_var

Otra ventaja de usar el índice de posición es que cuando desea citar un valor varias veces en la cadena de formato, solo necesita pasar un valor a través del método de formato. Se puede hacer referencia a este valor varias veces en la cadena de formato utilizando el mismo índice de posición.

formatted = '{0} loves food. See {0} cook.'.format(name)
print(formatted)

El resultado es el siguiente:

Max loves food. See Max cook.

Desafortunadamente, el método de formato no puede resolver el problema 2 anterior, por lo que es más difícil realizar pequeños cambios en el valor antes de formatear (debido a la necesidad de alinear la posición de los parámetros). El siguiente código compara el operador% con el método de formato. De hecho, tampoco es fácil de leer al mismo tiempo.

for i, (item, count) in enumerate(pantry):
    old_style = '#%d: %-10s = %d' % (
        i + 1,
        item.title(),
        round(count))
    new_style = '#{}: {:<10s} = {}'.format(
        i + 1,
        item.title(),
        round(count))


    assert old_style == new_style

Aunque el especificador de formato utilizado por el método de formato tiene opciones más avanzadas, como usar una combinación de claves de diccionario e índices de lista en marcadores de posición, y convertir valores en Unicode y repr strings:

formatted = 'First letter is {menu[oyster][0]!r}'.format(
    menu=menu)
print(formatted)

Los resultados son los siguientes:

First letter is 'k'

Sin embargo, estas funciones no pueden ayudar a reducir la redundancia de las claves duplicadas en la pregunta 4. Por ejemplo, aquí comparo la verbosidad de usar diccionarios en expresiones de formato de estilo C con el nuevo estilo de pasar el parámetro clave al método de formato:

old_template = (
    'Today\'s soup is %(soup)s, '
    'buy one get two %(oyster)s oysters, '
    'and our special entrée is %(special)s.')
old_formatted = template % {
    'soup': 'lentil',
    'oyster': 'kumamoto',
    'special': 'schnitzel',
}


new_template = (
    'Today\'s soup is {soup}, '
    'buy one get two {oyster} oysters, '
    'and our special entrée is {special}.')
new_formatted = new_template.format(
    soup='lentil',
    oyster='kumamoto',
    special='schnitzel',
)
assert old_formatted == new_formatted

Este estilo es menos ruidoso porque elimina algunas comillas en el diccionario y algunos caracteres en los especificadores de formato, pero no es perfecto. Además, las funciones avanzadas de usar claves de diccionario e índices en marcadores de posición proporcionan solo una pequeña parte de las capacidades de expresión de Python. Esta falta de expresividad hace que destruya el valor del método de formato en su conjunto.

Teniendo en cuenta estas deficiencias y el problema de las expresiones de formato de estilo C (preguntas 2 y 4 anteriores), mi sugerencia es evitar el uso del método str.format tanto como sea posible. Es muy importante comprender el nuevo mini lenguaje utilizado en los especificadores de formato (todo después de los dos puntos) y cómo utilizar las funciones integradas de formato.

3. cuerda f

Python 3.6 agrega una cadena de formato de interpolación (denominada cadena f) para resolver completamente estos problemas. Esta nueva sintaxis de idioma requiere que usted prefija la cadena de formato con el carácter f, que es similar a la cadena de bytes con el carácter b como prefijo, y la cadena original (sin escape) con el carácter r como prefijo.

La cadena f maximiza la expresividad de la cadena de formato y resuelve completamente el problema 4 al eliminar por completo la redundancia de proporcionar las claves y los valores que se formatearán. Hacen esto permitiéndole hacer referencia a todas las variables en el alcance actual de Python como parte de la expresión de formato:

key = 'my_var'
value = 1.234


formatted = f'{key} = {value}'
print(formatted)

El resultado es el siguiente:

my_var = 1.234

Se pueden usar las mismas opciones en el mini-lenguaje integrado formateado después de los dos puntos después del marcador de posición en la cadena f, o el valor se puede forzar a Unicode y reproducir cadenas de manera similar al método str.format:

formatted = f'{key!r:<10} = {value:.2f}'
print(formatted)

El resultado es el siguiente:

'my_var' = 1.23

En todos los casos, el formateo con cadenas f es más corto que el formateo con cadenas formateadas al estilo C con el operador% y el método str.format. Aquí, he mostrado todos estos métodos de formato en orden del más corto al más largo para que pueda compararlos fácilmente:

f_string = f'{key:<10} = {value:.2f}'


c_tuple  = '%-10s = %.2f' % (key, value)


str_args = '{:<10} = {:.2f}'.format(key, value)


str_kw   = '{key:<10} = {value:.2f}'.format(key=key,
                                          value=value)


c_dict   = '%(key)-10s = %(value).2f' % {'key': key,
                                       'value': value}


print(f'f_string:{f_string}')
print(f'c_tuple:{c_tuple}')
print(f'str_args:{str_args}')
print(f'str_kw:{str_kw}')
print(f'c_dict:{c_dict}')

El resultado es el siguiente:

f_string:my_var     = 1.23
c_tuple:my_var     = 1.23
str_args:my_var     = 1.23
str_kw:my_var     = 1.23
c_dict:my_var     = 1.23

La cadena f también puede poner la expresión de Python completa en los corchetes de marcador de posición, y haciendo pequeños cambios en el valor formateado con sintaxis concisa, el problema 2 se puede resolver fundamentalmente. Ahora, usar el formato de estilo C y el método str.format para gastar varias líneas de contenido ahora es fácil de poner en una sola línea:

for i, (item, count) in enumerate(pantry):
    old_style = '#%d: %-10s = %d' % (
        i + 1,
        item.title(),
        round(count))


    new_style = '#{}: {:<10s} = {}'.format(
        i + 1,
        item.title(),
        round(count))


   f_string = f'#{i+1}: {item.title():<10s} = {round(count)}'


   assert old_style == new_style == f_string

Por supuesto, si desea que el código sea más claro, puede dividir la cadena f en varias líneas. Incluso más largo que la versión de una sola línea, es mucho más limpio que cualquier otro método de varias líneas:

for i, (item, count) in enumerate(pantry):
    print(f'#{i+1}: '


          f'{item.title():<10s} = '
          f'{round(count)}')

El resultado es el siguiente:

#1: Avocados   = 1
#2: Bananas    = 2
#3: Cherries   = 15

Las expresiones de Python también pueden aparecer en las opciones de especificador de formato. Por ejemplo, aquí especifico el número de números de punto flotante a generar usando una variable en lugar de codificarla como una cadena de formato:

places = 3
number = 1.23456
print(f'My number is {number:.{places}f}')

Las cadenas f pueden combinar expresividad, simplicidad y claridad, lo que las convierte en las mejores opciones integradas para los programadores de Python. Siempre que encuentre que necesita formatear un valor como una cadena, puede elegir f-string como alternativa.

para resumir:

1. Las cadenas de formato de estilo C que utilizan el operador% encontrarán varias trampas y problemas prolongados;

2. El método str.format introduce algunos conceptos útiles en su mini-lenguaje de especificador de formato, pero repetirá el error de las cadenas de formato de estilo C en otros aspectos y debe evitarse;

3. f-string es una nueva gramática para formatear valores en cadenas, que resuelve el mayor problema de las cadenas de formato de estilo C;

4. Las f-strings son concisas y poderosas, porque permiten que expresiones arbitrarias de Python se incrusten directamente en especificadores de formato;

Si está interesado en este artículo, puede agregar la cuenta pública de WeChat del profesor Li Ning (unitymarvel):

Supongo que te gusta

Origin blog.csdn.net/nokiaguy/article/details/108722775
Recomendado
Clasificación